<?php
namespace Misuzu\Users;

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

class ModNotes {
    private IDbConnection $dbConn;
    private DbStatementCache $cache;

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

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

        $hasUserInfo = $userInfo !== null;
        $hasAuthorInfo = $authorInfo !== null;

        $args = 0;
        $query = 'SELECT COUNT(*) FROM msz_users_modnotes';
        if($hasUserInfo) {
            ++$args;
            $query .= ' WHERE user_id = ?';
        }
        if($hasAuthorInfo)
            $query .= sprintf(' %s author_id = ?', ++$args > 1 ? 'AND' : 'WHERE');

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

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

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

        return $count;
    }

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

        $hasUserInfo = $userInfo !== null;
        $hasAuthorInfo = $authorInfo !== null;
        $hasPagination = $pagination !== null;

        $args = 0;
        $query = 'SELECT note_id, user_id, author_id, UNIX_TIMESTAMP(note_created), note_title, note_body FROM msz_users_modnotes';
        if($hasUserInfo) {
            ++$args;
            $query .= ' WHERE user_id = ?';
        }
        if($hasAuthorInfo)
            $query .= sprintf(' %s author_id = ?', ++$args > 1 ? 'AND' : 'WHERE');
        $query .= ' ORDER BY note_created DESC';
        if($hasPagination)
            $query .= ' LIMIT ? OFFSET ?';

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

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

        while($result->next())
            $notes[] = new ModNoteInfo($result);

        return $notes;
    }

    public function getNote(string $noteId): ModNoteInfo {
        $stmt = $this->cache->get('SELECT note_id, user_id, author_id, UNIX_TIMESTAMP(note_created), note_title, note_body FROM msz_users_modnotes WHERE note_id = ?');
        $stmt->addParameter(1, $noteId);
        $stmt->execute();
        $result = $stmt->getResult();

        if(!$result->next())
            throw new RuntimeException('No note with ID $noteId found.');

        return new ModNoteInfo($result);
    }

    public function createNote(
        UserInfo|string $userInfo,
        string $title,
        string $body,
        UserInfo|string|null $authorInfo = null
    ): ModNoteInfo {
        if($userInfo instanceof UserInfo)
            $userInfo = $userInfo->getId();
        if($authorInfo instanceof UserInfo)
            $authorInfo = $authorInfo->getId();

        $stmt = $this->cache->get('INSERT INTO msz_users_modnotes (user_id, author_id, note_title, note_body) VALUES (?, ?, ?, ?)');
        $stmt->addParameter(1, $userInfo);
        $stmt->addParameter(2, $authorInfo);
        $stmt->addParameter(3, $title);
        $stmt->addParameter(4, $body);
        $stmt->execute();

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

    public function deleteNotes(ModNoteInfo|string|array $noteInfos): void {
        if(!is_array($noteInfos))
            $noteInfos = [$noteInfos];
        elseif(empty($noteInfos))
            return;

        $stmt = $this->cache->get(sprintf(
            'DELETE FROM msz_users_modnotes WHERE note_id IN (%s)',
            DbTools::prepareListString($noteInfos)
        ));

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

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

        $stmt->execute();
    }

    public function updateNote(
        ModNoteInfo|string $noteInfo,
        ?string $title = null,
        ?string $body = null
    ): void {
        if($noteInfo instanceof ModNoteInfo)
            $noteInfo = $noteInfo->getId();

        $stmt = $this->cache->get('UPDATE msz_users_modnotes SET note_title = COALESCE(?, note_title), note_body = COALESCE(?, note_body) WHERE note_id = ?');
        $stmt->addParameter(1, $title);
        $stmt->addParameter(2, $body);
        $stmt->addParameter(3, $noteInfo);
        $stmt->execute();
    }
}
