<?php

namespace app\foreignone;

use DateTime;
use engine\Model;
use engine\MySQL;
use Exception;
use app\MarkupUtil;

class Post
    extends Model
{
    const DB_TABLE = 'posts';

    public int $id;
    public string $date;
    public ?string $updateTime;
    public bool $visible;
    public string $shortName;
    public string $sourceUrl;

    protected array $texts = [];

    public function edit(array $fields) {
        $fields['update_time'] = date(MySQL::DATETIME_FORMAT, time());
        parent::edit($fields);
    }

    public function addText(PostLanguage $lang, string $title, string $md, string $keywords, bool $toc): ?PostText {
        $html = MarkupUtil::markdownToHtml($md, lang: $lang);
        $text = MarkupUtil::htmlToText($html);

        $data = [
            'title' => $title,
            'lang' => $lang->value,
            'post_id' => $this->id,
            'html' => $html,
            'text' => $text,
            'md' => $md,
            'toc' => $toc,
            'keywords' => $keywords,
        ];

        $db = getDB();
        if (!$db->insert('posts_texts', $data))
            return null;

        $id = $db->insertId();

        $post_text = PostText::get($id);
        $post_text->updateImagePreviews();

        return $post_text;
    }

    public function registerText(PostText $postText): void {
        if (array_key_exists($postText->lang->value, $this->texts))
            throw new Exception("text for language {$postText->lang->value} has already been registered");
        $this->texts[$postText->lang->value] = $postText;
    }

    public function loadTexts() {
        if (!empty($this->texts))
            return;
        $db = getDB();
        $q = $db->query("SELECT * FROM posts_texts WHERE post_id=?", $this->id);
        while ($row = $db->fetch($q)) {
            $text = new PostText($row);
            $this->registerText($text);
        }
    }

    /**
     * @return PostText[]
     */
    public function getTexts(): array {
        $this->loadTexts();
        return $this->texts;
    }

    public function getText(PostLanguage|string $lang): ?PostText {
        if (is_string($lang))
            $lang = PostLanguage::from($lang);
        $this->loadTexts();
        return $this->texts[$lang->value] ?? null;
    }

    public function hasLang(PostLanguage $lang) {
        $this->loadTexts();
        return array_any($this->texts, fn($text) => $text->lang == $lang);
    }

    public function hasSourceUrl(): bool {
        return $this->sourceUrl != '';
    }

    public function getUrl(PostLanguage|string|null $lang = null): string {
        $buf = $this->shortName != '' ? "/articles/{$this->shortName}/" : "/articles/{$this->id}/";
        if ($lang) {
            if (is_string($lang))
                $lang = PostLanguage::from($lang);
            if ($lang != PostLanguage::English)
                $buf .= '?lang='.$lang->value;
        }
        return $buf;
    }

    public function getTimestamp(): int {
        return new DateTime($this->date)->getTimestamp();
    }

    public function getUpdateTimestamp(): ?int {
        if (!$this->updateTime)
            return null;
        return new DateTime($this->updateTime)->getTimestamp();
    }

    public function getDate(): string {
        return date('j M', $this->getTimestamp());
    }

    public function getYear(): int {
        return (int)date('Y', $this->getTimestamp());
    }

    public function getFullDate(): string {
        return date('j F Y', $this->getTimestamp());
    }

    public function getDateForInputField(): string {
        return date('Y-m-d', $this->getTimestamp());
    }


    /**
     * Static methods
     */

    public static function getCount(bool $include_hidden = false): int {
        $db = getDB();
        $sql = "SELECT COUNT(*) FROM posts";
        if (!$include_hidden) {
            $sql .= " WHERE visible=1";
        }
        return (int)$db->result($db->query($sql));
    }

    /**
     * @return Post[]
     */
    public static function getList(int           $offset = 0,
                                   int           $count = -1,
                                   bool          $include_hidden = false,
                                   ?PostLanguage $filter_by_lang = null): array
    {
        $db = getDB();
        $sql = "SELECT * FROM posts";
        if (!$include_hidden)
            $sql .= " WHERE visible=1";
        $sql .= " ORDER BY `date` DESC";
        if ($offset != 0 || $count != -1)
            $sql .= " LIMIT $offset, $count";
        $q = $db->query($sql);
        $posts = [];
        while ($row = $db->fetch($q)) {
            $posts[$row['id']] = $row;
        }

        if (!empty($posts)) {
            foreach ($posts as &$post)
                $post = new Post($post);
            $q = $db->query("SELECT * FROM posts_texts WHERE post_id IN (".implode(',', array_keys($posts)).")");
            while ($row = $db->fetch($q)) {
                $posts[$row['post_id']]->registerText(new PostText($row));
            }
        }

        if ($filter_by_lang !== null)
            $posts = array_filter($posts, fn(Post $post) => $post->hasLang($filter_by_lang));

        return array_values($posts);
    }

    public static function add(array $data = []): ?Post {
        $db = getDB();
        if (!$db->insert('posts', $data))
            return null;
        return self::get($db->insertId());
    }

    public static function delete(Post $post): void {
        $db = getDB();
        $db->query("DELETE FROM posts WHERE id=?", $post->id);

        $text_ids = [];
        $q = $db->query("SELECT id FROM posts_texts WHERE post_id=?", $post->id);
        while ($row = $db->fetch($q))
            $text_ids = $row['id'];
        PreviousText::delete(PreviousText::TYPE_POST_TEXT, $text_ids);

        $db->query("DELETE FROM posts_texts WHERE post_id=?", $post->id);
    }

    public static function get(int $id): ?Post {
        $db = getDB();
        $q = $db->query("SELECT * FROM posts WHERE id=?", $id);
        return $db->numRows($q) ? new Post($db->fetch($q)) : null;
    }

    public static function getByName(string $short_name): ?Post {
        $db = getDB();
        $q = $db->query("SELECT * FROM posts WHERE short_name=?", $short_name);
        return $db->numRows($q) ? new Post($db->fetch($q)) : null;
    }

}