from __future__ import annotations

import re
from typing import Any


OUTERCOURSE_BOOBJOB = "boobjob"
OUTERCOURSE_TESTICLE = "testicle_sucking"
OUTERCOURSE_PENIS_LICKING = "penis_licking"
OUTERCOURSE_HANDJOB = "handjob"
OUTERCOURSE_FOOTJOB = "footjob"
OUTERCOURSE_GENERIC = "generic"


OUTERCOURSE_ACTION_KIND_CHOICES = {
    OUTERCOURSE_BOOBJOB,
    OUTERCOURSE_TESTICLE,
    OUTERCOURSE_PENIS_LICKING,
    OUTERCOURSE_HANDJOB,
    OUTERCOURSE_FOOTJOB,
    OUTERCOURSE_GENERIC,
}


def _clean(value: Any) -> str:
    text = "" if value is None else str(value)
    text = text.replace("\n", " ")
    text = re.sub(r"\s+", " ", text).strip()
    text = re.sub(r"\s+([,.;:])", r"\1", text)
    return text


def normalize_outercourse_action_kind(value: Any, default: str = OUTERCOURSE_GENERIC) -> str:
    text = re.sub(r"[^a-z0-9]+", "_", _clean(value).lower()).strip("_")
    aliases = {
        "breast_sex": OUTERCOURSE_BOOBJOB,
        "titjob": OUTERCOURSE_BOOBJOB,
        "tit_job": OUTERCOURSE_BOOBJOB,
        "testicle": OUTERCOURSE_TESTICLE,
        "testicles": OUTERCOURSE_TESTICLE,
        "ball_licking": OUTERCOURSE_TESTICLE,
        "balls_licking": OUTERCOURSE_TESTICLE,
        "balls": OUTERCOURSE_TESTICLE,
        "penis_lick": OUTERCOURSE_PENIS_LICKING,
        "penis_tongue": OUTERCOURSE_PENIS_LICKING,
        "hand_job": OUTERCOURSE_HANDJOB,
        "two_handed_handjob": OUTERCOURSE_HANDJOB,
        "foot_job": OUTERCOURSE_FOOTJOB,
        "feet_job": OUTERCOURSE_FOOTJOB,
    }
    text = aliases.get(text, text)
    return text if text in OUTERCOURSE_ACTION_KIND_CHOICES else default


def infer_outercourse_action_kind(*parts: Any) -> str:
    text = " ".join(_clean(part).lower() for part in parts if _clean(part))
    if not text:
        return OUTERCOURSE_GENERIC
    if any(
        term in text
        for term in (
            "boobjob",
            "titjob",
            "tit job",
            "breast sex",
            "breast-sex",
            "breasts tightly around",
            "breasts around",
            "breasts firmly together",
            "penis squeezed between both breasts",
            "penis shaft compressed between breasts",
            "soft flesh squeezed around the penis",
        )
    ):
        return OUTERCOURSE_BOOBJOB
    if any(
        term in text
        for term in (
            "testicle",
            "balls licking",
            "balls-licking",
            "balls held",
            "balls close",
            "balls and mouth",
            "mouth and tongue on the viewer's balls",
            "mouth and tongue on the pov viewer's balls",
            "mouth and tongue licking the viewer's balls",
            "mouth and tongue licking the pov viewer's balls",
        )
    ):
        return OUTERCOURSE_TESTICLE
    if any(
        term in text
        for term in (
            "penis licking",
            "penis-licking",
            "tongue along",
            "tongue runs along",
            "tongue running along",
            "tongue licking",
            "underside of the penis",
        )
    ):
        return OUTERCOURSE_PENIS_LICKING
    if any(
        term in text
        for term in (
            "handjob",
            "hand job",
            "hand wrapped",
            "hand wraps around",
            "hand stroking",
            "both hands stroking",
            "two-handed",
            "one hand grips",
            "one hand wrapped around",
        )
    ):
        return OUTERCOURSE_HANDJOB
    if any(
        term in text
        for term in (
            "footjob",
            "foot job",
            "soles",
            "toes curled",
            "feet stroking",
            "feet and penis",
            "both feet",
        )
    ):
        return OUTERCOURSE_FOOTJOB
    return OUTERCOURSE_GENERIC


def outercourse_context_text(*parts: Any) -> str:
    return " ".join(_clean(part).lower() for part in parts if _clean(part))
