"""Shared helpers for reading the WordPress WXR export."""

from __future__ import annotations

import re
import unicodedata
import xml.etree.ElementTree as ET
from dataclasses import dataclass, field
from pathlib import Path

NS = {
    "wp": "http://wordpress.org/export/1.2/",
    "content": "http://purl.org/rss/1.0/modules/content/",
    "excerpt": "http://wordpress.org/export/1.2/excerpt/",
    "dc": "http://purl.org/dc/elements/1.1/",
}

REPO_ROOT = Path(__file__).resolve().parent.parent
EXPORT = REPO_ROOT / "wordpress-export" / "motorradclubgiebelwaldev.WordPress.2026-07-28.xml"
CACHE = REPO_ROOT / ".cache" / "uploads"

# German transliteration first, so "Jubiläum" becomes "jubilaeum" and not "jubilaum".
UMLAUTS = {
    "ä": "ae", "ö": "oe", "ü": "ue", "ß": "ss",
    "Ä": "Ae", "Ö": "Oe", "Ü": "Ue",
}


def slugify(value: str, *, keep_dots: bool = False) -> str:
    for src, dst in UMLAUTS.items():
        value = value.replace(src, dst)
    value = unicodedata.normalize("NFKD", value)
    value = "".join(c for c in value if not unicodedata.combining(c))
    allowed = r"[^a-z0-9._-]+" if keep_dots else r"[^a-z0-9-]+"
    value = re.sub(allowed, "-", value.lower())
    return re.sub(r"-{2,}", "-", value).strip("-")


def slugify_filename(name: str) -> str:
    stem, _, ext = name.rpartition(".")
    return f"{slugify(stem, keep_dots=False)}.{ext.lower()}"


def clean_media_name(name: str) -> str:
    """Strip the bookkeeping suffixes WordPress bakes into upload filenames.

    ``Harry_2-e1654849333614.jpg`` -> ``harry-2.jpg``
    ``IMG_20220527_121440-small-scaled.jpg`` -> ``img-20220527-121440-small.jpg``
    """
    stem, _, ext = slugify_filename(name).rpartition(".")
    stem = re.sub(r"-e\d{9,}$", "", stem)      # post-crop revision id
    stem = re.sub(r"-\d+x\d+$", "", stem)      # generated size variant
    stem = re.sub(r"-scaled$", "", stem)       # WP "big image" downscale
    return f"{stem or 'bild'}.{ext}"


def text(node: ET.Element | None, path: str, default: str = "") -> str:
    if node is None:
        return default
    found = node.find(path, NS)
    if found is None or found.text is None:
        return default
    return found.text


@dataclass
class Item:
    element: ET.Element
    post_id: str
    post_type: str
    status: str
    title: str
    slug: str
    link: str
    parent: str
    date_gmt: str
    modified_gmt: str
    content: str
    excerpt: str
    attachment_url: str
    meta: dict[str, str] = field(default_factory=dict)
    categories: list[tuple[str, str]] = field(default_factory=list)


def load_items() -> dict[str, Item]:
    channel = ET.parse(EXPORT).getroot().find("channel")
    items: dict[str, Item] = {}
    for el in channel.findall("item"):
        meta = {}
        for m in el.findall("wp:postmeta", NS):
            key = text(m, "wp:meta_key")
            meta[key] = text(m, "wp:meta_value")
        cats = [
            (c.get("domain", ""), c.get("nicename", ""))
            for c in el.findall("category")
        ]
        item = Item(
            element=el,
            post_id=text(el, "wp:post_id"),
            post_type=text(el, "wp:post_type"),
            status=text(el, "wp:status"),
            title=(el.findtext("title") or "").strip(),
            slug=text(el, "wp:post_name"),
            link=(el.findtext("link") or "").strip(),
            parent=text(el, "wp:post_parent"),
            date_gmt=text(el, "wp:post_date_gmt"),
            modified_gmt=text(el, "wp:post_modified_gmt"),
            content=text(el, "content:encoded"),
            excerpt=text(el, "excerpt:encoded"),
            attachment_url=text(el, "wp:attachment_url"),
            meta=meta,
            categories=cats,
        )
        items[item.post_id] = item
    return items


def attachments(items: dict[str, Item]) -> dict[str, Item]:
    return {k: v for k, v in items.items() if v.post_type == "attachment"}


def cache_path(url: str) -> Path:
    """Local cache location mirroring the wp-content/uploads/YYYY/MM layout."""
    rel = url.split("/wp-content/uploads/", 1)[-1]
    parts = rel.split("/")
    return CACHE.joinpath(*parts[:-1], slugify_filename(parts[-1]))
