<?php
namespace Misuzu\Users;

use InvalidArgumentException;
use RuntimeException;
use Index\Data\DbStatementCache;
use Index\Data\DbTools;
use Index\Data\IDbConnection;
use Misuzu\Pagination;

// this system is currently kinda useless because it only silently shows up on profiles
// planning a notification system anyway so that should probably hook into

class Warnings {
    public const VISIBLE_BACKLOG = 90 * 24 * 60 * 60;

    private IDbConnection $dbConn;
    private DbStatementCache $cache;

    public function __construct(IDbConnection $dbConn) {
        $this->dbConn = $dbConn;
        $this->cache = new DbStatementCache($dbConn);
    }

    public function countWarnings(
        UserInfo|string|null $userInfo = null,
        ?int $backlog = null
    ): int {
        if($userInfo instanceof UserInfo)
            $userInfo = $userInfo->getId();

        $hasUserInfo = $userInfo !== null;
        $hasBacklog = $backlog !== null;

        $args = 0;
        $query = 'SELECT COUNT(*) FROM msz_users_warnings';
        if($hasUserInfo) {
            ++$args;
            $query .= ' WHERE user_id = ?';
        }
        if($hasBacklog) {
            if($backlog < 1)
                throw new InvalidArgumentException('$backlog must be either null to disable it or be greater than 0.');
            $query .= sprintf(' %s warn_created > NOW() - INTERVAL ? SECOND', ++$args > 1 ? 'AND' : 'WHERE');
        }

        $args = 0;
        $stmt = $this->cache->get($query);
        if($hasUserInfo)
            $stmt->addParameter(++$args, $userInfo);
        if($hasBacklog)
            $stmt->addParameter(++$args, $backlog);
        $stmt->execute();

        $result = $stmt->getResult();
        $count = 0;

        if($result->next())
            $count = $result->getInteger(0);

        return $count;
    }

    public function getWarningsWithDefaultBacklog(
        UserInfo|string|null $userInfo = null,
        ?Pagination $pagination = null
    ): array {
        return $this->getWarnings(
            $userInfo,
            self::VISIBLE_BACKLOG,
            $pagination
        );
    }

    public function getWarnings(
        UserInfo|string|null $userInfo = null,
        ?int $backlog = null,
        ?Pagination $pagination = null
    ): array {
        if($userInfo instanceof UserInfo)
            $userInfo = $userInfo->getId();

        $hasUserInfo = $userInfo !== null;
        $hasBacklog = $backlog !== null;
        $hasPagination = $pagination !== null;

        $args = 0;
        $query = 'SELECT warn_id, user_id, mod_id, warn_body, UNIX_TIMESTAMP(warn_created) FROM msz_users_warnings';
        if($hasUserInfo) {
            ++$args;
            $query .= ' WHERE user_id = ?';
        }
        if($hasBacklog) {
            if($backlog < 1)
                throw new InvalidArgumentException('$backlog must be either null to disable it or be greater than 0.');
            $query .= sprintf(' %s warn_created > NOW() - INTERVAL ? SECOND', ++$args > 1 ? 'AND' : 'WHERE');
        }
        $query .= ' ORDER BY warn_created DESC';
        if($hasPagination)
            $query .= ' LIMIT ? OFFSET ?';

        $args = 0;
        $stmt = $this->cache->get($query);
        if($hasUserInfo)
            $stmt->addParameter(++$args, $userInfo);
        if($hasBacklog)
            $stmt->addParameter(++$args, $backlog);
        if($hasPagination) {
            $stmt->addParameter(++$args, $pagination->getRange());
            $stmt->addParameter(++$args, $pagination->getOffset());
        }
        $stmt->execute();

        $result = $stmt->getResult();
        $warns = [];

        while($result->next())
            $warns[] = new WarningInfo($result);

        return $warns;
    }

    public function getWarning(string $warnId): WarningInfo {
        $stmt = $this->cache->get('SELECT warn_id, user_id, mod_id, warn_body, UNIX_TIMESTAMP(warn_created) FROM msz_users_warnings WHERE warn_id = ?');
        $stmt->addParameter(1, $warnId);
        $stmt->execute();

        $result = $stmt->getResult();

        if(!$result->next())
            throw new RuntimeException('Could not find warning info for ID $warnId.');

        return new WarningInfo($result);
    }

    public function createWarning(
        UserInfo|string $userInfo,
        string $body,
        UserInfo|string|null $modInfo
    ): WarningInfo {
        if($userInfo instanceof UserInfo)
            $userInfo = $userInfo->getId();
        if($modInfo instanceof UserInfo)
            $modInfo = $modInfo->getId();

        $stmt = $this->cache->get('INSERT INTO msz_users_warnings (user_id, mod_id, warn_body) VALUES (?, ?, ?)');
        $stmt->addParameter(1, $userInfo);
        $stmt->addParameter(2, $modInfo);
        $stmt->addParameter(3, $body);
        $stmt->execute();

        return $this->getWarning((string)$this->dbConn->getLastInsertId());
    }

    public function deleteWarnings(WarningInfo|string|array $warnInfos): void {
        if(!is_array($warnInfos))
            $warnInfos = [$warnInfos];
        elseif(empty($warnInfos))
            return;

        $stmt = $this->cache->get(sprintf(
            'DELETE FROM msz_users_warnings WHERE warn_id IN (%s)',
            DbTools::prepareListString($warnInfos)
        ));

        $args = 0;
        foreach($warnInfos as $warnInfo) {
            if($warnInfo instanceof WarningInfo)
                $warnInfo = $warnInfo->getId();
            elseif(!is_string($warnInfo))
                throw new InvalidArgumentException('$warnInfos must be strings of instances of WarningInfo.');

            $stmt->addParameter(++$args, $warnInfo);
        }

        $stmt->execute();
    }
}
