<?php

namespace app\foreignone;

use engine\Model;
use app\MarkupUtil;

class PostText
    extends Model
{
    const DB_TABLE = 'posts_texts';

    public int $id;
    public int $postId;
    public PostLanguage $lang;
    public string $title;
    public string $md;
    public string $html;
    public string $text;
    public bool $toc;
    public string $tocHtml;
    public string $keywords;

    public function edit(array $fields) {
        if ($fields['md'] != $this->md) {
            $fields['html'] = MarkupUtil::markdownToHtml($fields['md'], lang: $this->lang);
            $fields['text'] = MarkupUtil::htmlToText($fields['html']);
        }

        if ((isset($fields['toc']) && $fields['toc']) || $this->toc) {
            $fields['toc_html'] = MarkupUtil::toc($fields['md']);
        }

        parent::edit($fields);
        $this->updateImagePreviews();
    }

    public function updateHtml(): void {
        $html = MarkupUtil::markdownToHtml($this->md, lang: $this->lang);
        $this->html = $html;
        getDB()->query("UPDATE posts_texts SET html=? WHERE id=?", $html, $this->id);
    }

    public function updateText(): void {
        $html = MarkupUtil::markdownToHtml($this->md, lang: $this->lang);
        $text = MarkupUtil::htmlToText($html);
        $this->text = $text;
        getDB()->query("UPDATE posts_texts SET text=? WHERE id=?", $text, $this->id);
    }

    public function getDescriptionPreview(int $len): string {
        if (mb_strlen($this->text) >= $len)
            return mb_substr($this->text, 0, $len - 3).'...';
        return $this->text;
    }

    public function getFirstImage(): ?Upload {
        if (!preg_match('/\{image:(\w{8})/', $this->md, $match))
            return null;
        return Upload::getUploadByRandomId($match[1]);
    }

    public function getHtml(bool $is_retina, string $theme): string {
        $html = $this->html;
        return MarkupUtil::htmlImagesFix($html, $is_retina, $theme);
    }

    public function getTableOfContentsHtml(): ?string {
        return $this->tocHtml ?: null;
    }

    public function hasTableOfContents(): bool {
        return $this->toc;
    }

    /**
     * @param bool $update Whether to overwrite preview if already exists
     * @return int
     * @throws \Exception
     */
    public function updateImagePreviews(bool $update = false): int {
        $images = [];
        if (!preg_match_all('/\{image:(\w{8}),(.*?)}/', $this->md, $matches))
            return 0;

        for ($i = 0; $i < count($matches[0]); $i++) {
            $id = $matches[1][$i];
            $w = $h = null;
            $opts = explode(',', $matches[2][$i]);
            foreach ($opts as $opt) {
                if (str_contains($opt, '=')) {
                    list($k, $v) = explode('=', $opt);
                    if ($k == 'w')
                        $w = (int)$v;
                    else if ($k == 'h')
                        $h = (int)$v;
                }
            }
            $images[$id][] = [$w, $h];
        }

        if (empty($images))
            return 0;

        $images_affected = 0;
        $uploads = Upload::getUploadsByRandomId(array_keys($images), true);
        foreach ($uploads as $upload_key => $u) {
            if ($u === null) {
                logError(__METHOD__.': upload '.$upload_key.' is null');
                continue;
            }
            foreach ($images[$u->randomId] as $s) {
                list($w, $h) = $s;
                list($w, $h) = $u->getImagePreviewSize($w, $h);
                if ($u->createImagePreview($w, $h, $update, $u->imageMayHaveAlphaChannel()))
                    $images_affected++;
            }
        }

        return $images_affected;
    }


    /**
     * Static methods
     */

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

    public static function getPostTextsById(array $ids, bool $flat = false): array {
        if (empty($ids))
            return [];

        $db = getDB();
        $posts = array_fill_keys($ids, null);

        $q = $db->query("SELECT * FROM posts_texts WHERE id IN(".implode(',', $ids).")");

        while ($row = $db->fetch($q)) {
            $posts[(int)$row['id']] = new PostText($row);
        }

        if ($flat) {
            $list = [];
            foreach ($ids as $id) {
                $list[] = $posts[$id];
            }
            unset($posts);
            return $list;
        }

        return $posts;
    }

    /**
     * @param Upload $upload
     * @return PostText[] Array of PostTexts that includes specified upload
     */
    public static function getTextsWithUpload(Upload $upload): array {
        $db = getDB();
        $q = $db->query("SELECT id FROM posts_texts WHERE md LIKE '%{image:{$upload->randomId}%'");
        $ids = [];
        while ($row = $db->fetch($q))
            $ids[] = (int)$row['id'];
        return self::getPostTextsById($ids, true);
    }
}
