from __future__ import annotations

from typing import Any

try:
    from . import item_axis_policy
except ImportError:  # Allows local smoke tests with top-level imports.
    import item_axis_policy


SITTING_ORAL_VARIANT = "pov_blowjob_sitting_upright_oral"


def _context_text(item_text: str, item_axis_values: dict[str, Any] | None) -> str:
    return item_axis_policy.context_text(item_text, axis_values=item_axis_values)


def _oral_direction(text: str) -> tuple[bool, bool]:
    woman_gives = any(
        term in text
        for term in (
            "fellatio",
            "blowjob",
            "deepthroat",
            "penis sucking",
            "penis in mouth",
            "penis in her mouth",
            "mouth stretched around a penis",
            "lips wrapped",
        )
    )
    man_gives = any(
        term in text
        for term in (
            "cunnilingus",
            "pussy licking",
            "tongue on pussy",
            "mouth on pussy",
            "pussy and tongue",
            "face-sitting",
            "tongue contact clearly visible",
        )
    )
    if "mouth on genitals" in text and not woman_gives and not man_gives:
        if any(term in text for term in ("face-sitting", "reclining", "straddled", "spread-leg", "open thighs")):
            man_gives = True
        else:
            woman_gives = True
    return woman_gives, man_gives


def _list_values(value: Any) -> list[str]:
    if isinstance(value, list):
        return [str(item) for item in value if str(item).strip()]
    if isinstance(value, str) and value.strip():
        return [part.strip().strip("[]'\" ") for part in value.split(",") if part.strip().strip("[]'\" ")]
    return []


def _has_krea2_variant(axis_values: dict[str, Any] | None, key: str) -> bool:
    if not isinstance(axis_values, dict):
        return False
    return key in _list_values(axis_values.get("krea2_variant_keys"))


def build_oral_role_graph(
    woman: str,
    man: str,
    item_text: str,
    item_axis_values: dict[str, Any] | None = None,
    pov_labels: list[str] | None = None,
) -> str:
    position_text = item_axis_policy.key_text(item_axis_values, "position")
    text = _context_text(item_text, item_axis_values)
    man_is_pov = man in set(pov_labels or [])
    woman_gives, man_gives = _oral_direction(text)

    if _has_krea2_variant(item_axis_values, SITTING_ORAL_VARIANT):
        if man_is_pov:
            return (
                f"The viewer reclines with open thighs in an upright sitting oral frame while {woman} sits low between his thighs; "
                f"{woman}'s face lowers close to the centered shaft tip, her mouth on the viewer's penis, with both hands low at the base."
            )
        return (
            f"{man} reclines with open thighs in an upright sitting oral frame while {woman} sits low between his thighs; "
            f"{woman}'s face lowers close to the centered shaft tip, her mouth on his penis, with both hands low at the base."
        )
    if "sixty-nine" in position_text or ("sixty-nine" in text and not position_text):
        if man_is_pov:
            return (
                f"{woman} and the viewer lie head-to-hips in a sixty-nine position, "
                f"with {woman}'s mouth on the viewer's penis and the viewer's mouth on {woman}'s pussy."
            )
        return f"{woman} and {man} lie head-to-hips in a sixty-nine position, with {woman}'s mouth on {man}'s penis and {man}'s mouth on {woman}'s pussy."
    if "face-sitting" in position_text or ("face-sitting" in text and not position_text):
        if man_is_pov:
            return (
                f"{woman} is above the POV camera, straddling the viewer's face with thighs on both sides of his head, "
                "pussy directly over the viewer's mouth for close first-person underview tongue contact."
            )
        return f"{man} lies on his back while {woman} straddles his face with her thighs around his head and {man}'s mouth pressed to her pussy."
    if "straddled oral" in position_text or ("straddled oral" in text and not position_text):
        if woman_gives and not man_gives:
            if man_is_pov:
                return f"The viewer straddles forward near {woman}'s face while {woman} kneels below him with her mouth on his penis."
            return f"{man} straddles forward near {woman}'s face while {woman} kneels below him with her mouth on his penis."
        if man_is_pov:
            return f"{woman} straddles above the viewer's face with her thighs framing his head while the viewer's mouth stays pressed to her pussy."
        return f"{woman} straddles above {man}'s face with her thighs framing his head while {man}'s mouth stays pressed to her pussy."
    if "side-lying oral" in position_text or ("side-lying oral" in text and not position_text):
        if woman_gives and not man_gives:
            if man_is_pov:
                return (
                    f"The adult male viewer reclines in a first-person side-profile body-line view with the adult male viewer's abdomen, "
                    "navel, pelvis, and near thigh creating a broad horizontal body surface; the adult male viewer's own torso starts at the lower edge "
                    f"and runs diagonally into the lower-right foreground, with navel, abdomen hair, pelvis, and near thigh marking the camera owner's body; {woman} enters laterally from the left edge beside his hip, "
                    "cheek and jaw in profile, mouth on the shaft at the male abdomen line, lips touching the shaft at the male abdomen line, "
                    "mouth-to-shaft contact is the nearest facial detail, hand around the base under her lips, "
                    "shoulder and torso trailing sideways along the edge."
                )
            return f"{man} lies on his side with hips angled toward {woman} while {woman} lies beside his thighs and takes his penis in her mouth."
        if man_is_pov:
            return f"{woman} lies on her side with her top thigh lifted while the viewer lies beside her hips with his mouth pressed to her pussy."
        return f"{woman} lies on her side with her top thigh lifted while {man} lies beside her hips with his mouth pressed to her pussy."
    if (
        "edge-of-bed oral" in position_text
        or "edge of bed oral" in position_text
        or "edge-supported oral" in position_text
        or (("edge-of-bed oral" in text or "edge of bed oral" in text or "edge-supported oral" in text) and not position_text)
    ):
        if woman_gives and not man_gives:
            if man_is_pov:
                return f"The viewer sits at a raised edge with legs apart while {woman} kneels between his thighs and takes the viewer's penis in her mouth."
            return f"{man} sits at a raised edge with legs apart while {woman} kneels between his thighs and takes his penis in her mouth."
        if man_is_pov:
            return f"{woman} lies at a raised edge with thighs open while the viewer kneels between her legs with his mouth on her pussy."
        return f"{woman} lies at a raised edge with thighs open while {man} kneels between her legs with his mouth on her pussy."
    if "standing oral" in position_text or ("standing oral" in text and not position_text):
        if man_gives and not woman_gives:
            if man_is_pov:
                return f"{woman} stands braced with one thigh lifted while the viewer kneels between her legs with his mouth on her pussy."
            return f"{woman} stands braced with one thigh lifted while {man} kneels between her legs with his mouth on her pussy."
        if man_is_pov:
            return f"The viewer stands with hips forward while {woman} kneels in front of him at hip height and takes the viewer's penis in her mouth."
        return f"{man} stands with hips forward while {woman} kneels in front of him at hip height and takes his penis in her mouth."
    if "chair oral" in position_text or ("chair oral" in text and not position_text):
        if man_gives and not woman_gives:
            if man_is_pov:
                return f"{woman} sits in a chair with thighs open while the viewer kneels between her legs with his mouth pressed to her pussy."
            return f"{woman} sits in a chair with thighs open while {man} kneels between her legs with his mouth pressed to her pussy."
        if man_is_pov:
            return f"The viewer sits in a chair with legs apart while {woman} kneels between his thighs and takes the viewer's penis in her mouth."
        return f"{man} sits in a chair with legs apart while {woman} kneels between his thighs and takes his penis in her mouth."
    if (
        "reclining cunnilingus" in position_text
        or "spread-leg oral" in position_text
        or (("reclining cunnilingus" in text or "spread-leg oral" in text) and not position_text)
    ):
        if woman_gives and not man_gives:
            if man_is_pov:
                return f"The viewer reclines with legs apart while {woman} kneels between his thighs and takes the viewer's penis in her mouth."
            return f"{man} reclines with legs apart while {woman} kneels between his thighs and takes his penis in her mouth."
        if man_is_pov:
            return f"{woman} reclines on her back with thighs spread while the viewer kneels between her legs with his mouth on her pussy."
        return f"{woman} reclines on her back with thighs spread while {man} kneels between her legs with his mouth on her pussy."
    if "kneeling oral" in position_text or ("kneeling oral" in text and not position_text):
        if man_gives and not woman_gives:
            if man_is_pov:
                return f"{woman} kneels with thighs parted and hips angled forward while the viewer kneels in front of her with his mouth on her pussy."
            return f"{woman} kneels with thighs parted and hips angled forward while {man} kneels in front of her with his mouth on her pussy."
        if man_is_pov:
            return (
                f"{woman} kneels in front of the viewer's penis while he stands over her; "
                f"{woman} takes the viewer's penis in her mouth with saliva dripping on the penis as he looks down toward her."
            )
        return (
            f"{woman} kneels in front of {man}'s penis while {man} stands over her; "
            f"{woman} takes {man}'s penis in her mouth with saliva dripping on the penis as {man} looks down toward her."
        )
    if man_gives and not woman_gives:
        if man_is_pov:
            return f"{woman} lies on her back with thighs open while the viewer kneels between her legs with his mouth pressed to her pussy."
        return f"{woman} lies on her back with thighs open while {man} kneels between her legs with his mouth pressed to her pussy."
    if man_is_pov:
        return f"{woman} kneels in front of the viewer's hips and takes the viewer's penis in her mouth while he keeps his hips aligned with her face."
    return f"{woman} kneels in front of {man}'s hips and takes his penis in her mouth while {man} keeps his hips aligned with her face."
