from __future__ import annotations

import base64
import re
from typing import Iterable, List, Optional
from urllib.parse import urlparse

from .errors import ContentNotFoundError
from .types import PlayItem


AUDIO_HOST_RE = re.compile(r"_AUDIO_HTTP_HOST\s*=\s*'([^']+)'")
ONCLICK_PLAY_RE = re.compile(r'onclick="Play\((.*?)\);return false;"', re.DOTALL)
SINGLE_QUOTED_RE = re.compile(r"'([^']*)'")


def b64_decode(s: str) -> Optional[str]:
    try:
        padded = s + "=" * (-len(s) % 4)
        raw = base64.b64decode(padded)
        return raw.decode("utf-8")
    except Exception:
        return None


def extract_audio_host(html: str) -> str:
    m = AUDIO_HOST_RE.search(html)
    if not m:
        raise ContentNotFoundError("Could not find _AUDIO_HTTP_HOST in page HTML.")
    return m.group(1)


def safe_filename(name: str) -> str:
    name = name.strip()
    name = re.sub(r"\s+", "_", name)
    name = re.sub(r"[^a-zA-Z0-9._-]+", "_", name)
    return name.strip("_") or "forvo"


def page_slug(url: str) -> str:
    p = urlparse(url)
    bits = [b for b in p.path.split("/") if b]
    return safe_filename(bits[-1] if bits else "forvo")


def _dedupe_preserve_order(paths: Iterable[str]) -> List[str]:
    seen = set()
    uniq: List[str] = []
    for p in paths:
        if p not in seen:
            seen.add(p)
            uniq.append(p)
    return uniq


def _prefer_canonical(paths: List[str]) -> List[str]:
    paths.sort(key=lambda p: (0 if re.search(r"(^|/)2/s/2s_", p) else 1, len(p)))
    return paths


def iter_play_items(html: str) -> Iterable[PlayItem]:
    for m in ONCLICK_PLAY_RE.finditer(html):
        inside = m.group(1)

        play_id_match = re.match(r"\s*(\d+)\s*,", inside)
        play_id = play_id_match.group(1) if play_id_match else "unknown"

        quoted = SINGLE_QUOTED_RE.findall(inside)

        label = "forvo"
        if len(quoted) >= 2:
            label = quoted[-2] or quoted[-1] or "forvo"

        decoded_mp3_paths: List[str] = []
        for token in quoted:
            decoded = b64_decode(token)
            if decoded and ".mp3" in decoded:
                decoded_mp3_paths.append(decoded.lstrip("/"))

        uniq = _prefer_canonical(_dedupe_preserve_order(decoded_mp3_paths))

        if uniq:
            yield PlayItem(play_id=play_id, label=label, mp3_paths=tuple(uniq))
