<?php
namespace Domain\Pages;

class PagesRepository
{
    /**
     * @var array<int, string>
     */
    private const PAGE_TYPES = [
        0 => 'pelne artykuly',
        1 => 'wprowadzenia',
        2 => 'miniaturki',
        3 => 'link',
        4 => 'kontakt',
        5 => 'kategoria sklepu',
    ];

    /**
     * @var array<int, string>
     */
    private const SORT_TYPES = [
        0 => 'data dodania - najstarsze na poczatku',
        1 => 'data dodania - najnowsze na poczatku',
        2 => 'data modyfikacji - rosnaco',
        3 => 'data modyfikacji - malejaco',
        4 => 'reczne',
        5 => 'alfabetycznie - A - Z',
        6 => 'alfabetycznie - Z - A',
    ];

    private $db;

    public function __construct($db)
    {
        $this->db = $db;
    }

    /**
     * @return array<int, string>
     */
    public function pageTypes(): array
    {
        return self::PAGE_TYPES;
    }

    /**
     * @return array<int, string>
     */
    public function sortTypes(): array
    {
        return self::SORT_TYPES;
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function menusList(): array
    {
        $rows = $this->db->select('pp_menus', '*', ['ORDER' => ['id' => 'ASC']]);
        return is_array($rows) ? $rows : [];
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function menusWithPages(): array
    {
        $menus = $this->menusList();
        foreach ($menus as $index => $menu) {
            $menuId = (int)($menu['id'] ?? 0);
            $menus[$index]['pages'] = $this->menuPages($menuId);
        }

        return $menus;
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function menuPages(int $menuId, ?int $parentId = null): array
    {
        if ($menuId <= 0) {
            return [];
        }

        $rows = $this->db->select('pp_pages', ['id', 'menu_id', 'status', 'parent_id', 'start'], [
            'AND' => [
                'menu_id' => $menuId,
                'parent_id' => $parentId,
            ],
            'ORDER' => ['o' => 'ASC'],
        ]);

        if (!is_array($rows)) {
            return [];
        }

        $pages = [];
        foreach ($rows as $row) {
            $pageId = (int)($row['id'] ?? 0);
            if ($pageId <= 0) {
                continue;
            }

            $row['title'] = $this->pageTitle($pageId);
            $row['languages'] = $this->pageLanguages($pageId);
            $row['subpages'] = $this->menuPages($menuId, $pageId);
            $pages[] = $row;
        }

        return $pages;
    }

    public function menuDelete(int $menuId): bool
    {
        if ($menuId <= 0) {
            return false;
        }

        if ((int)$this->db->count('pp_pages', ['menu_id' => $menuId]) > 0) {
            return false;
        }

        return (bool)$this->db->delete('pp_menus', ['id' => $menuId]);
    }

    public function pageDelete(int $pageId): bool
    {
        if ($pageId <= 0) {
            return false;
        }

        if ((int)$this->db->count('pp_pages', ['parent_id' => $pageId]) > 0) {
            return false;
        }

        return (bool)$this->db->delete('pp_pages', ['id' => $pageId]);
    }

    /**
     * @return array<string, mixed>
     */
    public function menuDetails(int $menuId): array
    {
        if ($menuId <= 0) {
            return [
                'id' => 0,
                'name' => '',
                'status' => 1,
            ];
        }

        $menu = $this->db->get('pp_menus', '*', ['id' => $menuId]);
        if (!is_array($menu)) {
            return [
                'id' => 0,
                'name' => '',
                'status' => 1,
            ];
        }

        return $menu;
    }

    public function menuSave(int $menuId, string $name, $status): bool
    {
        $statusValue = $this->toSwitchValue($status);

        if ($menuId <= 0) {
            $result = $this->db->insert('pp_menus', [
                'name' => $name,
                'status' => $statusValue,
            ]);

            if ($result) {
                \Shared\Helpers\Helpers::delete_dir('../temp/');
            }
            return (bool)$result;
        }

        $this->db->update('pp_menus', [
            'name' => $name,
            'status' => $statusValue,
        ], [
            'id' => $menuId,
        ]);

        \Shared\Helpers\Helpers::delete_dir('../temp/');
        return true;
    }

    /**
     * @return array<string, mixed>
     */
    public function pageDetails(int $pageId): array
    {
        if ($pageId <= 0) {
            return $this->defaultPage();
        }

        $page = $this->db->get('pp_pages', '*', ['id' => $pageId]);
        if (!is_array($page)) {
            return $this->defaultPage();
        }

        $translations = $this->db->select('pp_pages_langs', '*', ['page_id' => $pageId]);
        if (is_array($translations)) {
            foreach ($translations as $row) {
                $langId = (string)($row['lang_id'] ?? '');
                if ($langId !== '') {
                    $page['languages'][$langId] = $row;
                }
            }
        }

        $page['layout_id'] = (int)$this->db->get('pp_layouts_pages', 'layout_id', ['page_id' => $pageId]);

        return $page;
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function pageArticles(int $pageId): array
    {
        if ($pageId <= 0) {
            return [];
        }

        $sql = '
            SELECT
                ap.article_id,
                ap.o,
                a.status,
                (
                    SELECT title
                    FROM pp_articles_langs AS pal
                    JOIN pp_langs AS pl ON pal.lang_id = pl.id
                    WHERE pal.article_id = ap.article_id
                        AND pal.title != ""
                    ORDER BY pl.o ASC
                    LIMIT 1
                ) AS title
            FROM pp_articles_pages AS ap
            JOIN pp_articles AS a ON a.id = ap.article_id
            WHERE ap.page_id = :page_id
                AND a.status != -1
            ORDER BY ap.o ASC
        ';

        $stmt = $this->db->query($sql, [':page_id' => $pageId]);
        $rows = $stmt ? $stmt->fetchAll() : [];

        return is_array($rows) ? $rows : [];
    }

    public function saveArticlesOrder(int $pageId, $articles): bool
    {
        if ($pageId <= 0) {
            return false;
        }

        if (!is_array($articles)) {
            return true;
        }

        $this->db->update('pp_articles_pages', ['o' => 0], ['page_id' => $pageId]);

        $position = 0;
        foreach ($articles as $item) {
            $articleId = (int)($item['item_id'] ?? 0);
            if ($articleId <= 0) {
                continue;
            }

            $position++;
            $this->db->update('pp_articles_pages', ['o' => $position], [
                'AND' => [
                    'page_id' => $pageId,
                    'article_id' => $articleId,
                ],
            ]);
        }

        \Shared\Helpers\Helpers::delete_dir('../temp/');
        return true;
    }

    public function savePagesOrder(int $menuId, $pages): bool
    {
        if ($menuId <= 0) {
            return false;
        }

        if (!is_array($pages)) {
            return true;
        }

        $this->db->update('pp_pages', ['o' => 0], ['menu_id' => $menuId]);

        $position = 0;
        foreach ($pages as $item) {
            $itemId = (int)($item['item_id'] ?? 0);
            $depth = (int)($item['depth'] ?? 0);
            if ($itemId <= 0 || $depth <= 1) {
                continue;
            }

            $parentId = (int)($item['parent_id'] ?? 0);
            if ($depth === 2) {
                $parentId = null;
            }

            $position++;
            $this->db->update('pp_pages', [
                'o' => $position,
                'parent_id' => $parentId,
            ], [
                'id' => $itemId,
            ]);
        }

        \Shared\Helpers\Helpers::delete_dir('../temp/');

        return true;
    }

    public function pageSave(array $data): ?int
    {
        $pageId = (int)($data['id'] ?? 0);
        $menuId = (int)($data['menu_id'] ?? 0);
        $parentId = $this->normalizeNullableInt($data['parent_id'] ?? null);
        $pageType = (int)($data['page_type'] ?? 0);
        $sortType = (int)($data['sort_type'] ?? 0);
        $layoutId = (int)($data['layout_id'] ?? 0);
        $articlesLimit = (int)($data['articles_limit'] ?? 0);
        $showTitle = $this->toSwitchValue($data['show_title'] ?? 0);
        $status = $this->toSwitchValue($data['status'] ?? 0);
        $start = $this->toSwitchValue($data['start'] ?? 0);
        $categoryId = $this->normalizeNullableInt($data['category_id'] ?? null);
        if ($pageType !== 5) {
            $categoryId = null;
        }

        if ($pageId <= 0) {
            $order = $this->maxPageOrder() + 1;
            $result = $this->db->insert('pp_pages', [
                'menu_id' => $menuId,
                'page_type' => $pageType,
                'sort_type' => $sortType,
                'articles_limit' => $articlesLimit,
                'show_title' => $showTitle,
                'status' => $status,
                'o' => $order,
                'parent_id' => $parentId,
                'start' => $start,
                'category_id' => $categoryId,
            ]);

            if (!$result) {
                return null;
            }

            $pageId = (int)$this->db->id();
            if ($pageId <= 0) {
                return null;
            }
        } else {
            $this->db->update('pp_pages', [
                'menu_id' => $menuId,
                'page_type' => $pageType,
                'sort_type' => $sortType,
                'articles_limit' => $articlesLimit,
                'show_title' => $showTitle,
                'status' => $status,
                'parent_id' => $parentId,
                'start' => $start,
                'category_id' => $categoryId,
            ], [
                'id' => $pageId,
            ]);
        }

        if ($start === 1) {
            $this->db->update('pp_pages', ['start' => 0], ['id[!]' => $pageId]);
            $this->db->update('pp_pages', ['start' => 1], ['id' => $pageId]);
        }

        $this->db->delete('pp_layouts_pages', ['page_id' => $pageId]);
        if ($layoutId > 0) {
            $this->db->insert('pp_layouts_pages', [
                'layout_id' => $layoutId,
                'page_id' => $pageId,
            ]);
        }

        $this->saveTranslations($pageId, $pageType, $data);
        $this->updateSubpagesMenuId($pageId, $menuId);

        \Shared\Helpers\Helpers::htacces();
        \Shared\Helpers\Helpers::delete_dir('../temp/');

        return $pageId;
    }

    public function generateSeoLink(string $title, int $pageId = 0, int $articleId = 0, int $categoryId = 0): string
    {
        $base = trim((string)\Shared\Helpers\Helpers::seo($title));
        if ($base === '') {
            return '';
        }

        $candidate = $base;
        $suffix = 0;

        while ($this->isSeoLinkUsed('pp_pages_langs', 'page_id', $candidate, $pageId)
            || $this->isSeoLinkUsed('pp_articles_langs', 'article_id', $candidate, $articleId)
            || $this->isSeoLinkUsed('pp_shop_categories_langs', 'category_id', $candidate, $categoryId)) {
            $suffix++;
            $candidate = $base . '-' . $suffix;
        }

        return $candidate;
    }

    public function pageUrlPreview(int $pageId, string $langId, string $title, string $seoLink, string $defaultLanguageId): string
    {
        $url = trim($seoLink) !== ''
            ? '/' . ltrim($seoLink, '/')
            : '/s-' . $pageId . '-' . \Shared\Helpers\Helpers::seo($title);

        if ($langId !== '' && $langId !== $defaultLanguageId && $url !== '#') {
            $url = '/' . $langId . $url;
        }

        return $url;
    }

    public function toggleCookieValue(string $cookieName, int $itemId): void
    {
        if ($cookieName === '' || $itemId <= 0) {
            return;
        }

        $state = [];
        if (!empty($_COOKIE[$cookieName])) {
            $decoded = @unserialize((string)$_COOKIE[$cookieName], ['allowed_classes' => false]);
            if (is_array($decoded)) {
                $state = $decoded;
            }
        }

        $state[$itemId] = empty($state[$itemId]) ? 1 : 0;
        setcookie($cookieName, serialize($state), time() + 3600 * 24 * 365);
    }

    public function pageTitle(int $pageId): string
    {
        if ($pageId <= 0) {
            return '';
        }

        $rows = $this->db->select('pp_pages_langs', [
            '[><]pp_langs' => ['lang_id' => 'id'],
        ], 'title', [
            'AND' => [
                'page_id' => $pageId,
                'title[!]' => '',
            ],
            'ORDER' => ['o' => 'ASC'],
            'LIMIT' => 1,
        ]);

        if (!is_array($rows) || !isset($rows[0])) {
            return '';
        }

        return (string)$rows[0];
    }

    /**
     * @return array<int, array<string, mixed>>
     */
    public function pageLanguages(int $pageId): array
    {
        if ($pageId <= 0) {
            return [];
        }

        $rows = $this->db->select('pp_pages_langs', '*', [
            'AND' => [
                'page_id' => $pageId,
                'title[!]' => null,
            ],
        ]);

        return is_array($rows) ? $rows : [];
    }

    /**
     * @return array<string, mixed>
     */
    private function defaultPage(): array
    {
        return [
            'id' => 0,
            'menu_id' => 0,
            'page_type' => 0,
            'sort_type' => 0,
            'articles_limit' => 2,
            'show_title' => 1,
            'status' => 1,
            'start' => 0,
            'parent_id' => null,
            'category_id' => null,
            'layout_id' => 0,
            'languages' => [],
        ];
    }

    private function saveTranslations(int $pageId, int $pageType, array $data): void
    {
        $titles = is_array($data['title'] ?? null) ? $data['title'] : [];
        $seoLinks = is_array($data['seo_link'] ?? null) ? $data['seo_link'] : [];
        $metaTitles = is_array($data['meta_title'] ?? null) ? $data['meta_title'] : [];
        $metaDescriptions = is_array($data['meta_description'] ?? null) ? $data['meta_description'] : [];
        $metaKeywords = is_array($data['meta_keywords'] ?? null) ? $data['meta_keywords'] : [];
        $noindexValues = is_array($data['noindex'] ?? null) ? $data['noindex'] : [];
        $pageTitles = is_array($data['page_title'] ?? null) ? $data['page_title'] : [];
        $links = is_array($data['link'] ?? null) ? $data['link'] : [];
        $canonicals = is_array($data['canonical'] ?? null) ? $data['canonical'] : [];

        foreach ($titles as $langId => $title) {
            $langId = (string)$langId;
            if ($langId === '') {
                continue;
            }

            $row = [
                'lang_id' => $langId,
                'title' => $this->nullIfEmpty($title),
                'meta_description' => $this->nullIfEmpty($metaDescriptions[$langId] ?? null),
                'meta_keywords' => $this->nullIfEmpty($metaKeywords[$langId] ?? null),
                'meta_title' => $this->nullIfEmpty($metaTitles[$langId] ?? null),
                'seo_link' => $this->nullIfEmpty(\Shared\Helpers\Helpers::seo((string)($seoLinks[$langId] ?? ''))),
                'noindex' => (int)($noindexValues[$langId] ?? 0),
                'page_title' => $this->nullIfEmpty($pageTitles[$langId] ?? null),
                'link' => $pageType === 3 ? $this->nullIfEmpty($links[$langId] ?? null) : null,
                'canonical' => $this->nullIfEmpty($canonicals[$langId] ?? null),
            ];

            $translationId = (int)$this->db->get('pp_pages_langs', 'id', [
                'AND' => [
                    'page_id' => $pageId,
                    'lang_id' => $langId,
                ],
            ]);

            if ($translationId > 0) {
                $this->db->update('pp_pages_langs', $row, ['id' => $translationId]);
            } else {
                $row['page_id'] = $pageId;
                $this->db->insert('pp_pages_langs', $row);
            }
        }
    }

    private function updateSubpagesMenuId(int $parentId, int $menuId): void
    {
        if ($parentId <= 0 || $menuId <= 0) {
            return;
        }

        $this->db->update('pp_pages', ['menu_id' => $menuId], ['parent_id' => $parentId]);

        $children = $this->db->select('pp_pages', ['id'], ['parent_id' => $parentId]);
        if (!is_array($children)) {
            return;
        }

        foreach ($children as $row) {
            $childId = (int)($row['id'] ?? 0);
            if ($childId > 0) {
                $this->updateSubpagesMenuId($childId, $menuId);
            }
        }
    }

    private function isSeoLinkUsed(string $table, string $idColumn, string $seoLink, int $exceptId): bool
    {
        $where = [
            'seo_link' => $seoLink,
        ];

        if ($exceptId > 0) {
            $where[$idColumn . '[!]'] = $exceptId;
        }

        return (int)$this->db->count($table, ['AND' => $where]) > 0;
    }

    private function maxPageOrder(): int
    {
        $max = $this->db->max('pp_pages', 'o');
        return $max ? (int)$max : 0;
    }

    private function toSwitchValue($value): int
    {
        if ($value === 'on' || $value === '1' || $value === 1 || $value === true) {
            return 1;
        }

        return 0;
    }

    private function normalizeNullableInt($value): ?int
    {
        if ($value === null || $value === '' || (int)$value === 0) {
            return null;
        }

        return (int)$value;
    }

    private function nullIfEmpty($value): ?string
    {
        $value = trim((string)$value);
        return $value === '' ? null : $value;
    }

    // ── Frontend methods ──────────────────────────────────────────

    public function frontPageDetails($id = '', $langId = ''): ?array
    {
        $langId = (string)$langId;

        if (!$id) {
            $id = $this->frontMainPageId();
        }

        $cacheHandler = new \Shared\Cache\CacheHandler();
        $cacheKey = "PagesRepository::frontPageDetails:$id:$langId";

        $objectData = $cacheHandler->get($cacheKey);
        if ($objectData) {
            $cached = @unserialize($objectData);
            if (is_array($cached)) {
                return $cached;
            }
            $cacheHandler->delete($cacheKey);
        }

        $page = $this->db->get('pp_pages', '*', ['id' => (int)$id]);
        if (!is_array($page)) {
            return null;
        }

        $page['language'] = $this->db->get('pp_pages_langs', '*', ['AND' => ['page_id' => (int)$id, 'lang_id' => $langId]]);

        $cacheHandler->set($cacheKey, $page);
        return $page;
    }

    public function frontPageSort(int $pageId)
    {
        $cacheHandler = new \Shared\Cache\CacheHandler();
        $cacheKey = "PagesRepository::frontPageSort:$pageId";

        $objectData = $cacheHandler->get($cacheKey);
        if ($objectData) {
            $cached = @unserialize($objectData);
            if ($cached !== false) {
                return $cached;
            }
            $cacheHandler->delete($cacheKey);
        }

        $sort = $this->db->get('pp_pages', 'sort_type', ['id' => $pageId]);

        $cacheHandler->set($cacheKey, $sort);
        return $sort;
    }

    public function frontMainPageId()
    {
        $cacheHandler = new \Shared\Cache\CacheHandler();
        $cacheKey = 'PagesRepository::frontMainPageId';

        $objectData = $cacheHandler->get($cacheKey);
        if ($objectData) {
            $cached = @unserialize($objectData);
            if ($cached) {
                return $cached;
            }
            $cacheHandler->delete($cacheKey);
        }

        $id = $this->db->get('pp_pages', 'id', ['AND' => ['status' => 1, 'start' => 1]]);
        if (!$id) {
            $id = $this->db->get('pp_pages', 'id', ['status' => 1, 'ORDER' => ['menu_id' => 'ASC', 'o' => 'ASC'], 'LIMIT' => 1]);
        }

        $cacheHandler->set($cacheKey, $id);
        return $id;
    }

    public function frontLangUrl(int $pageId, string $langId): string
    {
        $page = $this->frontPageDetails($pageId, $langId);
        if (!is_array($page) || !is_array($page['language'] ?? null)) {
            return '/';
        }

        $seoLink = $page['language']['seo_link'] ?? '';
        $title = $page['language']['title'] ?? '';
        $url = $seoLink ? '/' . $seoLink : '/s-' . $page['id'] . '-' . \Shared\Helpers\Helpers::seo($title);

        $defaultLang = (new \Domain\Languages\LanguagesRepository($this->db))->defaultLanguage();
        if ($langId !== $defaultLang && $url !== '#') {
            $url = '/' . $langId . $url;
        }

        return $url;
    }

    public function frontMenuDetails(int $menuId, string $langId): ?array
    {
        $cacheHandler = new \Shared\Cache\CacheHandler();
        $cacheKey = "PagesRepository::frontMenuDetails:$menuId:$langId";

        $objectData = $cacheHandler->get($cacheKey);
        if ($objectData) {
            $cached = @unserialize($objectData);
            if (is_array($cached)) {
                return $cached;
            }
            $cacheHandler->delete($cacheKey);
        }

        $menu = $this->db->get('pp_menus', '*', ['id' => (int)$menuId]);
        if (!is_array($menu)) {
            return null;
        }

        $menu['pages'] = $this->frontMenuPages($menuId, $langId);

        $cacheHandler->set($cacheKey, $menu);
        return $menu;
    }

    public function frontMenuPages(int $menuId, string $langId, $parentId = null): ?array
    {
        $cacheHandler = new \Shared\Cache\CacheHandler();
        $cacheKey = "PagesRepository::frontMenuPages:$menuId:$langId:$parentId";

        $objectData = $cacheHandler->get($cacheKey);
        if ($objectData) {
            $cached = @unserialize($objectData);
            if (is_array($cached)) {
                return $cached;
            }
            $cacheHandler->delete($cacheKey);
        }

        $results = $this->db->select('pp_pages', ['id'], [
            'AND' => ['status' => 1, 'menu_id' => (int)$menuId, 'parent_id' => $parentId],
            'ORDER' => ['o' => 'ASC'],
        ]);

        $pages = [];
        if (is_array($results)) {
            foreach ($results as $row) {
                $page = $this->frontPageDetails($row['id'], $langId);
                if (is_array($page)) {
                    $page['pages'] = $this->frontMenuPages($menuId, $langId, $row['id']);
                    $pages[] = $page;
                }
            }
        }

        $cacheHandler->set($cacheKey, $pages);
        return $pages;
    }
}
