"""Inferencja tagów z tytułu sceny.

Kontekst: porn-app `VideoInformation` zwraca tylko podstawowe pola (title, link, img,
duration, quality, pornstars) — bez tagów. ~7000 scen w DB z tube'ów ma 0-1 tagów,
przez co filtrowanie wg tagu (anal/big-tits/...) zwraca prawie wyłącznie sceny TPDB,
których z kolei prawie żadna nie ma żywego playback.

Rozwiązanie: dla każdej sceny wyciągamy z tytułu wzmianki o popularnych kategoriach.
Słownik jest celowo ostrożny — tylko phrasy które rzadko fałszywie się dopasują (full
word boundaries), pokrywające ~30-40% kategorialnych tagów (anal/blowjob/lesbian/itp.).
Wizualne atrybuty (brown-hair/tattoos/lingerie) świadomie pominięte — z tytułu nie da
się ich odgadnąć.

Slug-i są zgrane z istniejącymi w DB (np. "milf-30" zamiast "milf", bo TPDB taki
ustanowił). Inference dorzuca SceneTag do scen porn-app — gdy potem resolver merguje
porn-app scenę z TPDB sceną, tagi z TPDB i tagi z inference się sumują (set union),
więc inference nie nadpisuje, tylko uzupełnia.
"""
from __future__ import annotations

import re

# Map: canonical slug → lista phrasów (lowercase). Każdy phrase matchuje się word
# boundaries — więc "anal" w "fundamental" NIE łapie, ale "anal sex" czy "Anal." tak.
# Phrasy z dwóch słów dopasowują się z tolerancją na wieloznaki spacji/kresek/podkreśleń
# (`anal_creampie`, `anal-creampie`, `anal creampie` — wszystkie matchują wzorzec).
TAG_PHRASES: dict[str, list[str]] = {
    "anal": [
        "anal", "anally", "anus", "asshole", "ass fuck", "butt fuck", "ass to mouth",
        "atm", "buttplug", "anally fucked",
    ],
    "anal-creampie": ["anal creampie", "anal cream pie", "creampied ass", "ass creampie"],
    "anal-fisting": ["anal fisting", "anal fist", "fisting anal"],
    "anal-gape": ["anal gape", "gaping ass", "ass gape"],
    "double-anal-penetration-dap": ["double anal", "dap", "dual anal", "two cocks in ass"],
    "big-tits": [
        "big tits", "big tit", "big boobs", "big boob", "big breasts",
        "huge tits", "huge tit", "huge boobs", "huge boob", "busty",
        "big naturals", "massive tits", "bigtits", "bigtit",
    ],
    "natural-tits": ["natural tits", "natural tit", "natural boobs", "all natural"],
    "small-tits": ["small tits", "small tit", "tiny tits", "flat chested", "petite"],
    "fake-tits": ["fake tits", "fake boobs", "silicone tits", "implants"],
    "big-ass": [
        "big ass", "big butt", "huge ass", "fat ass", "pawg", "phat ass",
        "thicc", "thicc ass", "big booty", "jiggly ass",
    ],
    "blowjob": [
        "blowjob", "blow job", "blowjobs", "bj", "cock sucking", "sucking cock",
        "sucking dick", "dick sucking", "throat fuck",
    ],
    "deepthroat": ["deepthroat", "deep throat", "deepthroats", "throat goat"],
    "face-fuck": ["face fuck", "facefuck", "throat fuck"],
    "facial": ["facial", "cum on face", "facial cumshot", "facials"],
    "cumshot": ["cumshot", "cum shot", "cumshots", "cum load"],
    "creampie": ["creampie", "cream pie", "creampies", "cum inside"],
    "cum-on-tits": ["cum on tits", "cumshot on tits", "tit cum"],
    "milf-30": [
        "milf", "milfs", "mom", "moms", "mommy", "stepmom", "step mom",
        "stepmother", "step mother", "hot mom", "mature woman", "cougar",
        "stepmommy",
    ],
    "lesbian": [
        "lesbian", "lesbians", "girl on girl", "girls only", "lez",
        "all girl", "lesbo",
    ],
    "threesome": [
        "threesome", "3some", "three some", "mfm", "mff", "fmf", "ffm",
        "two girls one guy", "two guys one girl", "ffm threesome",
    ],
    "foursome": ["foursome", "4some", "four some", "two couples"],
    "gangbang": [
        "gangbang", "gang bang", "orgy", "group sex", "bukkake",
    ],
    "interracial": [
        "interracial", "bbc", "big black cock", "ir", "black on white",
    ],
    "rough": ["rough sex", "rough", "brutal", "punish", "punished", "extreme"],
    "bdsm": [
        "bdsm", "bondage", "submission", "dominatrix", "femdom", "tied up",
        "tied", "slave", "domination",
    ],
    "masturbation": [
        "masturbation", "masturbating", "masturbate", "solo", "fingering herself",
        "jerk off", "jerking off", "jerking",
    ],
    "pov": ["pov", "point of view", "first person"],
    "doggy-style": ["doggy", "doggystyle", "doggy style", "from behind", "doggie style"],
    "cowgirl": ["cowgirl", "riding cock", "riding dick"],
    "reverse-cowgirl": ["reverse cowgirl", "reverse-cowgirl", "rcg"],
    "rimming": ["rimming", "rim job", "rimjob", "ass licking", "ass-licking"],
    "handjob": ["handjob", "hand job", "hj", "tugging"],
    "footjob": ["footjob", "foot job"],
    "feet": ["foot fetish", "feet fetish", "barefoot"],
    "pussy-licking": ["pussy licking", "pussy lick", "eating pussy", "pussy eating"],
    "fisting": ["fisting", "fist fuck", "fistfuck", "fisted"],
    "squirt": ["squirt", "squirting", "squirts", "female ejaculation"],
    "big-dick": [
        "big dick", "big cock", "huge dick", "huge cock", "monster cock",
        "monster dick", "bigcock", "bigdick",
    ],
    "lingerie": ["lingerie", "stockings", "fishnets", "garter"],
    "public": ["in public", "public sex", "outdoor sex", "outdoors", "public flashing"],
    "ebony": ["ebony", "black girl", "black girls"],
    "asian": [
        "asian", "japanese", "korean", "chinese", "thai", "vietnamese",
        "japanese girl", "asian girl",
    ],
    "latina": ["latina", "latin girl", "latinas"],
    "teen-18": [
        "teen", "teens", "18yo", "18 year old", "18-year", "teenage",
        "barely legal", "young 18",
    ],
    "amateur": ["amateur", "homemade", "home made", "real amateur"],
    "hardcore": ["hardcore", "hard core"],
    "step-fantasy": [
        "stepsister", "step sister", "stepsis", "stepbrother", "step brother",
        "stepson", "step son", "stepdad", "step dad", "stepfather", "step father",
        "stepmom", "step mom", "step daughter", "stepdaughter",
    ],
    "anal-toys": ["anal toy", "butt plug", "buttplug", "anal beads"],
    "anal-fingering": ["anal fingering", "finger ass", "ass fingering"],
    "tribbing": ["tribbing", "trib", "scissoring"],
    "double-penetration": [
        "double penetration", "dp scene", "dped",
    ],
    "hairy": ["hairy pussy", "hairy bush", "unshaven", "natural bush"],
    "tattoo": ["tattooed", "tattoos", "inked"],
    "european": ["czech girl", "british girl", "german girl", "russian girl", "euro porn"],
    "cuckold": ["cuckold", "hotwife", "wife watching", "cuck"],
    "swingers": ["swingers", "swinging couple", "wife swap"],
    "gloryhole": ["gloryhole", "glory hole"],
    "pissing": ["pissing", "watersports"],
    "bbw": ["bbw", "chubby", "plus size"],
}


def _build_pattern() -> tuple[re.Pattern[str], list[tuple[int, str]]]:
    """Buduje 1 regex z named groups dla wszystkich phrasów. Match O(N) na tytułu.

    Phrasa multiword: spacje jako `[\\s_-]+` (tolerancja na separator). Single word:
    word boundary `\\b...\\b`. Słownik: `pattern → slug` żeby zmapować named groups
    z powrotem na slugi.
    """
    parts: list[str] = []
    pattern_to_slug: list[tuple[int, str]] = []
    idx = 0
    for slug, phrases in TAG_PHRASES.items():
        for phrase in phrases:
            words = phrase.split()
            if len(words) == 1:
                # \b<word>\b — działa też dla słów zaczynających/kończących się na non-alnum.
                pat = rf"\b{re.escape(words[0])}\b"
            else:
                pat = r"\b" + r"[\s_-]+".join(re.escape(w) for w in words) + r"\b"
            parts.append(f"(?P<g{idx}>{pat})")
            pattern_to_slug.append((idx, slug))
            idx += 1
    combined = re.compile("|".join(parts), re.IGNORECASE)
    return combined, pattern_to_slug


_PATTERN, _PATTERN_TO_SLUG = _build_pattern()
_GROUP_TO_SLUG = dict(_PATTERN_TO_SLUG)


def infer_tag_slugs(text: str | None) -> list[str]:
    """Zwraca posortowaną listę unikalnych canonical tag slugów wykrytych w tytule.

    Kolejność: alfabetyczna (deterministyczne — łatwiej testować, brak wpływu na DB).
    """
    if not text:
        return []
    found: set[str] = set()
    for m in _PATTERN.finditer(text):
        for name, value in m.groupdict().items():
            if value is None:
                continue
            idx = int(name[1:])  # 'g42' → 42
            slug = _GROUP_TO_SLUG.get(idx)
            if slug:
                found.add(slug)
    return sorted(found)


__all__ = ["infer_tag_slugs", "TAG_PHRASES"]
