<?php
namespace Domain\Dictionaries;

class DictionariesRepository
{
    private const MAX_PER_PAGE = 100;
    private const CACHE_TTL = 86400;
    private const CACHE_SUBDIR = 'dictionaries';

    private $db;

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

    public function listForAdmin(
        array $filters,
        string $sortColumn = 'id',
        string $sortDir = 'ASC',
        int $page = 1,
        int $perPage = 15
    ): array {
        $allowedSortColumns = [
            'id' => 'u.id',
            'text' => 'text',
        ];

        $sortSql = $allowedSortColumns[$sortColumn] ?? 'u.id';
        $sortDir = strtoupper(trim($sortDir)) === 'DESC' ? 'DESC' : 'ASC';
        $page = max(1, $page);
        $perPage = min(self::MAX_PER_PAGE, max(1, $perPage));
        $offset = ($page - 1) * $perPage;

        $where = ['1=1'];
        $params = [];

        $text = trim((string)($filters['text'] ?? ''));
        if (strlen($text) > 255) {
            $text = substr($text, 0, 255);
        }

        if ($text !== '') {
            $where[] = 'EXISTS (SELECT 1 FROM pp_units_langs AS ul2 WHERE ul2.unit_id = u.id AND ul2.text LIKE :text)';
            $params[':text'] = '%' . $text . '%';
        }

        $whereSql = implode(' AND ', $where);

        $sqlCount = "
            SELECT COUNT(0)
            FROM pp_units AS u
            WHERE {$whereSql}
        ";

        $stmtCount = $this->db->query($sqlCount, $params);
        $countRows = $stmtCount ? $stmtCount->fetchAll() : [];
        $total = isset($countRows[0][0]) ? (int)$countRows[0][0] : 0;

        $sql = "
            SELECT
                u.*,
                (
                    SELECT ul.text
                    FROM pp_units_langs AS ul
                    INNER JOIN pp_langs AS l ON l.id = ul.lang_id
                    WHERE ul.unit_id = u.id AND ul.text <> ''
                    ORDER BY l.o ASC
                    LIMIT 1
                ) AS text
            FROM pp_units AS u
            WHERE {$whereSql}
            ORDER BY {$sortSql} {$sortDir}, u.id {$sortDir}
            LIMIT {$perPage} OFFSET {$offset}
        ";

        $stmt = $this->db->query($sql, $params);
        $items = $stmt ? $stmt->fetchAll() : [];

        return [
            'items' => is_array($items) ? $items : [],
            'total' => $total,
        ];
    }

    public function allUnits(): array
    {
        $sql = "
            SELECT
                u.*,
                (
                    SELECT ul.text
                    FROM pp_units_langs AS ul
                    INNER JOIN pp_langs AS l ON l.id = ul.lang_id
                    WHERE ul.unit_id = u.id AND ul.text <> ''
                    ORDER BY l.o ASC
                    LIMIT 1
                ) AS text
            FROM pp_units AS u
            ORDER BY u.id ASC
        ";

        $stmt = $this->db->query($sql);
        $rows = $stmt ? $stmt->fetchAll(\PDO::FETCH_ASSOC) : [];
        if (!is_array($rows)) {
            return [];
        }

        $units = [];
        foreach ($rows as $row) {
            $units[(int)$row['id']] = $row;
        }

        return $units;
    }

    public function find(int $unitId): ?array
    {
        $unit = $this->db->get('pp_units', '*', ['id' => $unitId]);
        if (!$unit) {
            return null;
        }

        $unit['languages'] = [];
        $translations = $this->db->select('pp_units_langs', '*', [
            'unit_id' => $unitId,
            'ORDER' => ['lang_id' => 'ASC', 'id' => 'ASC'],
        ]);

        if (is_array($translations)) {
            foreach ($translations as $row) {
                $unit['languages'][(string)$row['lang_id']] = $row;
            }
        }

        return $unit;
    }

    public function save(array $data)
    {
        $unitId = isset($data['id']) ? (int)$data['id'] : 0;

        if ($unitId <= 0) {
            $this->db->insert('pp_units', []);
            $unitId = (int)$this->db->id();
            if ($unitId <= 0) {
                return false;
            }
        }

        $translations = $this->normalizeTranslations($data);
        foreach ($translations as $langId => $text) {
            $this->upsertTranslation($unitId, $langId, $text);
        }

        $this->clearCache();

        return $unitId;
    }

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

        $this->db->delete('pp_units_langs', ['unit_id' => $unitId]);
        $result = $this->db->delete('pp_units', ['id' => $unitId]);

        $this->clearCache();

        return $result !== false;
    }

    public function getUnitNameById(int $unitId, $langId): string
    {
        $langId = trim((string)$langId);
        if ($unitId <= 0 || $langId === '') {
            return '';
        }

        $cacheKey = "get_name_by_id:$unitId:$langId";
        $unitName = $this->cacheFetch($cacheKey);

        if ($unitName === false) {
            $unitName = $this->db->get('pp_units_langs', 'text', [
                'AND' => [
                    'unit_id' => $unitId,
                    'lang_id' => $langId,
                ],
            ]);

            $this->cacheStore($cacheKey, $unitName ?: '');
        }

        return (string)$unitName;
    }

    private function normalizeTranslations(array $data): array
    {
        $translations = [];

        if (isset($data['translations']) && is_array($data['translations'])) {
            foreach ($data['translations'] as $langId => $fields) {
                $translations[(string)$langId] = trim((string)($fields['text'] ?? ''));
            }
            return $translations;
        }

        if (isset($data['text']) && is_array($data['text'])) {
            foreach ($data['text'] as $langId => $text) {
                $translations[(string)$langId] = trim((string)$text);
            }
        }

        return $translations;
    }

    private function upsertTranslation(int $unitId, $langId, string $text): void
    {
        $langId = trim((string)$langId);
        if ($langId === '') {
            return;
        }

        $translationId = $this->db->get('pp_units_langs', 'id', [
            'AND' => [
                'unit_id' => $unitId,
                'lang_id' => $langId,
            ],
        ]);

        if ($translationId) {
            $this->db->update('pp_units_langs', [
                'text' => $text,
            ], [
                'id' => (int)$translationId,
            ]);
            return;
        }

        $this->db->insert('pp_units_langs', [
            'unit_id' => $unitId,
            'lang_id' => $langId,
            'text' => $text,
        ]);
    }

    private function clearCache(): void
    {
        if (class_exists('\S') && method_exists('\S', 'delete_dir')) {
            \Shared\Helpers\Helpers::delete_dir('../temp/');
            \Shared\Helpers\Helpers::delete_dir('../temp/dictionaries');
        }
    }

    private function cacheFetch(string $key)
    {
        $cacheHandler = new \Shared\Cache\CacheHandler();
        $cached = $cacheHandler->get(self::CACHE_SUBDIR . ':' . $key);
        if ($cached) {
            return unserialize($cached);
        }
        return false;
    }

    private function cacheStore(string $key, string $value): void
    {
        $cacheHandler = new \Shared\Cache\CacheHandler();
        $cacheHandler->set(self::CACHE_SUBDIR . ':' . $key, $value, self::CACHE_TTL);
    }
}
