from __future__ import annotations

from hashlib import sha1
from html import unescape
from pathlib import Path
import re
import shutil

from bs4 import BeautifulSoup

from models import InventoryItem, norm

_BG_RE = re.compile(r"url\((?:'|\")?([^)'\"]+)(?:'|\")?\)", re.I)


def _style_url(node) -> str:
    if node is None:
        return ""
    match = _BG_RE.search(node.get("style", ""))
    return unescape(match.group(1)).strip() if match else ""


def _files_dir(html_path: Path) -> Path | None:
    candidates = [
        html_path.with_name(html_path.stem + "_files"),
        html_path.with_name(html_path.name.rsplit(".", 1)[0] + "_files"),
    ]
    candidates.extend(sorted(html_path.parent.glob("*_files")))
    for candidate in candidates:
        if candidate.is_dir():
            return candidate
    return None


def _resolve_local(url: str, files_dir: Path | None) -> Path | None:
    if not url or not files_dir:
        return None
    name = Path(url.split("?", 1)[0]).name
    direct = files_dir / name
    if direct.is_file():
        return direct
    # Chrome occasionally appends suffixes to duplicate names.
    matches = list(files_dir.glob(name + "*"))
    return matches[0] if matches else None


def _copy_cached(source: Path | None, cache_dir: Path, prefix: str) -> str:
    if not source or not source.is_file():
        return ""
    cache_dir.mkdir(parents=True, exist_ok=True)
    digest = sha1(source.read_bytes()).hexdigest()[:16]
    suffix = source.suffix.lower() or ".img"
    destination = cache_dir / f"{prefix}_{digest}{suffix}"
    if not destination.exists():
        shutil.copy2(source, destination)
    return str(destination)


def extract_armory_images(html_path: Path, cache_dir: Path) -> tuple[dict[str, tuple[str, str, str]], dict[str, tuple[str, str, str]]]:
    """Return image mappings by item id and normalised full name.

    BloodWars stores the visual paths inside escaped ``data-tippy-content``
    fragments. Saved complete pages flatten those remote assets into the adjacent
    ``*_files`` directory, so paths are resolved by basename and copied to the
    app cache before the source export can be deleted.
    """
    html_path = Path(html_path)
    soup = BeautifulSoup(html_path.read_text(encoding="utf-8", errors="replace"), "html.parser")
    files_dir = _files_dir(html_path)
    by_id: dict[str, tuple[str, str, str]] = {}
    by_name: dict[str, tuple[str, str, str]] = {}

    for node in soup.select("[data-tippy-content]"):
        raw = node.get("data-tippy-content") or ""
        if "item-descContainer" not in raw or "itemMainImageContainer" not in raw:
            continue
        frag = BeautifulSoup(unescape(raw), "html.parser")
        container = frag.select_one("[id^='item-descContainer_']")
        if not container:
            continue
        item_id = container.get("id", "").rsplit("_", 1)[-1]
        name_node = frag.select_one(".item-name")
        item_name = name_node.get_text(" ", strip=True) if name_node else ""
        main_url = _style_url(frag.select_one(".itemMainImageContainer"))
        affixes = frag.select(".itemAffixContainer")
        prefix_url = _style_url(affixes[0]) if len(affixes) > 0 else ""
        suffix_url = _style_url(affixes[1]) if len(affixes) > 1 else ""
        images = (
            _copy_cached(_resolve_local(main_url, files_dir), cache_dir, "item"),
            _copy_cached(_resolve_local(prefix_url, files_dir), cache_dir, "prefix"),
            _copy_cached(_resolve_local(suffix_url, files_dir), cache_dir, "suffix"),
        )
        if any(images):
            if item_id:
                by_id[item_id] = images
            if item_name:
                by_name[norm(item_name)] = images
    return by_id, by_name


def enrich_inventory_images(items: list[InventoryItem], html_path: Path, cache_dir: Path) -> None:
    by_id, by_name = extract_armory_images(html_path, cache_dir)
    for item in items:
        images = by_id.get(str(item.item_id)) or by_name.get(norm(item.full_name))
        if not images:
            continue
        item.main_image, item.prefix_image, item.suffix_image = images
