"""Fetch live supermarket folders from reclamefolder.nl — all chains."""
from __future__ import annotations

import json
import re
import xml.etree.ElementTree as ET
from datetime import date, datetime, timezone
from typing import Any, Optional
from urllib.parse import unquote
from urllib.request import HTTPCookieProcessor, Request, build_opener

from app.db import execute, fetch_all, fetch_one

BASE = "https://www.reclamefolder.nl"
SUPERMARKT_URL = f"{BASE}/categorieen/supermarkt/"
SITEMAP_RETAILERS = f"{BASE}/sitemap/retailers.xml"
USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36"

SUPERMARKT_KEYWORDS = (
    "supermarkt", "markt", "ah", "jumbo", "lidl", "aldi", "plus", "dirk",
    "coop", "boni", "vomar", "deka", "ekoplaza", "mitra", "spar", "hoogvliet",
    "food", "gall", "poiesz", "nettorama",
)

_opener = None


def _get_opener():
    global _opener
    if _opener is None:
        _opener = build_opener(HTTPCookieProcessor())
    return _opener


def _fetch_html(url: str) -> str:
    headers = {"User-Agent": USER_AGENT, "Accept-Language": "nl-NL,nl;q=0.9"}
    html = _get_opener().open(Request(url, headers=headers), timeout=35).read().decode("utf-8", "ignore")
    if "__NEXT_DATA__" not in html:
        cb = re.search(r"decodeURIComponent\('([^']+)'\)", html)
        if cb:
            html = _get_opener().open(Request(unquote(cb.group(1)), headers=headers), timeout=35).read().decode("utf-8", "ignore")
    return html


def _fetch_page_props(url: str) -> dict[str, Any]:
    html = _fetch_html(url)
    match = re.search(r'<script id="__NEXT_DATA__" type="application/json">(.+?)</script>', html)
    if not match:
        return {}
    data = json.loads(match.group(1))
    return data.get("props", {}).get("pageProps", {}) or {}


def _parse_day(raw: Optional[str]) -> Optional[date]:
    if not raw:
        return None
    try:
        return datetime.fromisoformat(raw.replace("Z", "+00:00")).date()
    except Exception:
        return None


def _folder_item(row: dict[str, Any], source: str = "category") -> Optional[dict[str, Any]]:
    retailer = row.get("retailer") or {}
    if source == "retailer_page":
        chain = retailer.get("name") or row.get("name", "")
        edition_id = row.get("id")
        valid_label = ""
        cover = row.get("cover") or {}
        folder_name = row.get("name") or "Folder"
    else:
        chain = retailer.get("name")
        edition_id = row.get("editionId") or row.get("id")
        valid_label = row.get("validLabel") or ""
        cover = row.get("cover") or {}
        folder_name = valid_label or "Folder"

    if not edition_id or not chain:
        return None

    valid_to = _parse_day(row.get("validThru"))
    today = datetime.now(timezone.utc).date()
    if valid_to and valid_to < today:
        return None

    permaname = retailer.get("permaname") or ""
    url = f"{BASE}/f/folders/{edition_id}/"
    title = f"{chain} — {folder_name}" if folder_name != chain else f"{chain} folder ({valid_label})".strip(" ()")

    return {
        "chain": chain,
        "title": title,
        "folder_path": url,
        "folder_label": valid_label or folder_name or chain,
        "description": f"Reclamefolder.nl · {valid_label or folder_name}".strip(" ·"),
        "valid_from": _parse_day(row.get("validFrom")),
        "valid_to": valid_to,
        "image_url": cover.get("imageUrl") if isinstance(cover, dict) else None,
        "status": "active",
        "promo_type": row.get("name") or "folder",
        "source": "reclamefolder.nl",
        "metadata": {
            "edition_id": str(edition_id),
            "version_id": str(row.get("versionId") or row.get("id") or ""),
            "retailer_permaname": permaname,
            "retailer_url": f"{BASE}/winkels/{permaname}/" if permaname else "",
            "folder_type": row.get("name") or "folder",
        },
    }


def fetch_retailer_slugs() -> list[str]:
    try:
        req = Request(SITEMAP_RETAILERS, headers={"User-Agent": USER_AGENT})
        data = _get_opener().open(req, timeout=45).read()
        text = data.decode("utf-8", "ignore")
        slugs = set()
        for m in re.finditer(r"https://www\.reclamefolder\.nl/winkels/([a-z0-9-]+)/", text):
            slug = m.group(1)
            if "vestiging" in slug:
                continue
            if any(k in slug for k in SUPERMARKET_KEYWORDS):
                slugs.add(slug)
        return sorted(slugs)
    except Exception:
        return [
            "albert-heijn", "jumbo", "lidl", "plus", "dirk", "aldi", "dekamarkt",
            "ekoplaza", "vomar", "coop-supermarkten", "boni-supermarkt", "mitra",
        ]


def fetch_supermarkt_folders(include_all_retailers: bool = True) -> list[dict[str, Any]]:
    seen: set[str] = set()
    items: list[dict[str, Any]] = []

    props = _fetch_page_props(SUPERMARKT_URL)
    for row in props.get("foldersFromProps") or []:
        item = _folder_item(row, "category")
        if item and item["metadata"]["edition_id"] not in seen:
            seen.add(item["metadata"]["edition_id"])
            items.append(item)

    if include_all_retailers:
        for slug in fetch_retailer_slugs():
            try:
                rprops = _fetch_page_props(f"{BASE}/winkels/{slug}/")
                retailer = rprops.get("retailer") or {}
                for folder in retailer.get("folders") or []:
                    folder = dict(folder)
                    folder["retailer"] = retailer
                    item = _folder_item(folder, "retailer_page")
                    if item and item["metadata"]["edition_id"] not in seen:
                        seen.add(item["metadata"]["edition_id"])
                        items.append(item)
            except Exception:
                continue

    items.sort(key=lambda x: (x.get("chain") or "", x.get("valid_to") or date.max))
    return items


def sync_to_db() -> dict[str, Any]:
    folders = fetch_supermarkt_folders()
    execute(
        "UPDATE promo_campaigns SET status = 'expired', updated_at = NOW() WHERE source = 'reclamefolder.nl'"
    )
    inserted = 0
    chains: set[str] = set()
    for f in folders:
        fetch_one(
            """INSERT INTO promo_campaigns
               (chain, title, folder_path, folder_label, description, valid_from, valid_to,
                status, promo_type, image_url, source, metadata)
               VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s::jsonb)
               RETURNING id""",
            (
                f["chain"], f["title"], f["folder_path"], f["folder_label"],
                f["description"], f["valid_from"], f["valid_to"],
                f["status"], f["promo_type"], f.get("image_url"),
                f["source"], json.dumps(f["metadata"]),
            ),
        )
        inserted += 1
        chains.add(f["chain"])
    return {
        "ok": True,
        "source": "reclamefolder.nl",
        "synced": inserted,
        "chains": len(chains),
        "items": folders,
    }


def list_cached(limit: int = 200) -> list[dict[str, Any]]:
    rows = fetch_all(
        """SELECT * FROM promo_campaigns
           WHERE source = 'reclamefolder.nl' AND status = 'active'
           ORDER BY valid_to ASC NULLS LAST, chain ASC
           LIMIT %s""",
        (limit,),
    )
    return [dict(r) for r in rows]


def list_chains() -> list[str]:
    rows = fetch_all(
        """SELECT DISTINCT chain FROM promo_campaigns
           WHERE source = 'reclamefolder.nl' AND status = 'active' AND chain IS NOT NULL
           ORDER BY chain"""
    )
    return [r["chain"] for r in rows if r.get("chain")]
