from __future__ import annotations

import json
from hashlib import sha1
from pathlib import Path
from urllib.parse import urlparse

from PySide6.QtCore import Qt
from PySide6.QtGui import QPixmap

from awakening_theme import asset_path, resource_dir


class ImageRepository:
    def __init__(self, cache_dir: Path):
        self.cache_dir = Path(cache_dir)
        self.cache_dir.mkdir(parents=True, exist_ok=True)
        self.official_dir = self.cache_dir / "official"
        self.official_dir.mkdir(parents=True, exist_ok=True)
        self._pixmaps: dict[tuple[str, int, int], QPixmap] = {}
        self.pack_dir = resource_dir() / "assets" / "item_pack"
        self.registry = self._load_registry()

    def _load_registry(self) -> dict:
        path = self.pack_dir / "registry.json"
        if not path.is_file():
            return {"bases": {}, "main_variants": {}, "prefixes": {}, "suffixes": {}, "remote": {}}
        try:
            data = json.loads(path.read_text(encoding="utf-8"))
        except Exception:
            return {"bases": {}, "main_variants": {}, "prefixes": {}, "suffixes": {}, "remote": {}}
        for key in ("bases", "main_variants", "prefixes", "suffixes", "remote"):
            data.setdefault(key, {})
        return data

    @staticmethod
    def _existing(path: str | Path | None) -> Path | None:
        if not path:
            return None
        value = Path(path)
        return value if value.is_file() else None

    def _pack_path(self, kind: str, key: str) -> Path | None:
        rel = self.registry.get(kind, {}).get(key)
        if not rel:
            return None
        path = self.pack_dir / rel
        return path if path.is_file() else None

    @staticmethod
    def semantic_key(category: str, component: str) -> str:
        return f"{category}|{component}"

    def bundled_main(self, category: str, base_key: str, quality: str = "", legendary: bool = False) -> Path | None:
        if quality:
            variant_key = f"{category}|{base_key}|{quality}|{1 if legendary else 0}"
            variant = self._pack_path("main_variants", variant_key)
            if variant:
                return variant
        return self._pack_path("bases", self.semantic_key(category, base_key))

    def bundled_prefix(self, category: str, prefix_key: str) -> Path | None:
        return self._pack_path("prefixes", self.semantic_key(category, prefix_key))

    def bundled_suffix(self, category: str, suffix_key: str) -> Path | None:
        return self._pack_path("suffixes", self.semantic_key(category, suffix_key))

    def remote_url(self, kind: str, category: str, component: str) -> str:
        return self.registry.get("remote", {}).get(f"{kind}|{category}|{component}", "")

    def cached_path_for_url(self, url: str, kind: str) -> Path | None:
        if not url:
            return None
        suffix = Path(urlparse(url).path).suffix.lower() or ".img"
        path = self.official_dir / f"{kind}_{sha1(url.encode('utf-8')).hexdigest()[:20]}{suffix}"
        return path if path.is_file() else None

    def fallback_main(self, category: str = "", base: str = "", *, base_key: str = "", quality: str = "", legendary: bool = False) -> Path:
        bundled = self.bundled_main(category, base_key, quality, legendary) if base_key else None
        if bundled:
            return bundled
        base_map = {
            "helm": "helm_bd_2_ZTF.jpg",
            "kask": "helm_bd_2_ZTF.jpg",
            "czapka": "helm_bd_2_ZTF.jpg",
            "kamizelka": "kamizelka_bd_2_LLD.jpg",
            "zbroja": "kamizelka_bd_2_LLD.jpg",
            "spodnie": "spodnie_bd_3_4GM.jpg",
            "szorty": "spodnie_bd_3_4GM.jpg",
            "pierscien": "pierscien_d_2_DAR.jpg",
            "sygnet": "pierscien_d_2_DAR.jpg",
            "krawat": "krawatdqz9.jpg",
            "amulet": "krawatdqz9.jpg",
            "ciezka kusza": "t_ciezkakuszabdpt5.jpg",
            "kusza": "t_ciezkakuszabdpt5.jpg",
        }
        key = base.casefold().translate(str.maketrans("łęśóżźćąń", "lesozzcan"))
        name = base_map.get(key)
        if not name:
            cat_map = {
                "head": "helm_bd_2_ZTF.jpg", "torso": "kamizelka_bd_2_LLD.jpg",
                "pants": "spodnie_bd_3_4GM.jpg", "rings": "pierscien_d_2_DAR.jpg",
                "amulets": "krawatdqz9.jpg", "ranged": "t_ciezkakuszabdpt5.jpg",
            }
            name = cat_map.get(category, "prefEmpty.jpg")
        return asset_path(name)

    def fallback_prefix(self, prefix: str = "", *, category: str = "", prefix_key: str = "") -> Path:
        bundled = self.bundled_prefix(category, prefix_key) if prefix_key else None
        if bundled:
            return bundled
        mapping = {
            "lekki": "1lekka_KQC.jpg", "lekka": "1lekka_KQC.jpg", "lekkie": "1lekka_KQC.jpg",
            "pomocny": "1pomocny_64V.jpg", "pomocna": "1pomocny_64V.jpg",
        }
        return asset_path(mapping.get(prefix.casefold(), "prefEmpty.jpg"))

    def fallback_suffix(self, suffix: str = "", *, category: str = "", suffix_key: str = "") -> Path:
        bundled = self.bundled_suffix(category, suffix_key) if suffix_key else None
        if bundled:
            return bundled
        mapping = {
            "kobry": "2kobry_5GI.jpg", "lewitacji": "2lewitacja_EF2.jpg", "unikow": "2unikow_HJ3.jpg",
            "uników": "2unikow_HJ3.jpg",
        }
        return asset_path(mapping.get(suffix.casefold(), "sufEmpty.jpg"))

    def pixmap(self, path: str | Path | None, width: int, height: int, fallback: Path | None = None) -> QPixmap:
        existing = self._existing(path) or self._existing(fallback)
        key = (str(existing or ""), width, height)
        if key in self._pixmaps:
            return self._pixmaps[key]
        pix = QPixmap(str(existing)) if existing else QPixmap()
        if pix.isNull():
            pix = QPixmap(width, height)
            pix.fill(Qt.transparent)
        else:
            pix = pix.scaled(width, height, Qt.KeepAspectRatio, Qt.SmoothTransformation)
        self._pixmaps[key] = pix
        return pix
