from __future__ import annotations

import re
from dataclasses import dataclass
from typing import Any

try:
    from .krea_action_context import (
        axis_values_text,
        is_climax_text,
        is_toy_assisted_double_text,
        normalize_hardcore_detail_density,
    )
    from .hardcore_action_metadata import (
        ACTION_ANAL,
        ACTION_CLIMAX,
        ACTION_FOREPLAY,
        ACTION_MANUAL,
        ACTION_ORAL,
        ACTION_OUTERCOURSE,
        ACTION_PENETRATION,
        ACTION_TOY_DOUBLE,
        infer_hardcore_action_family,
        normalize_hardcore_action_family,
    )
    from .krea_detail import limit_detail_for_density
    from .krea_action_positions import hardcore_pose_anchor
    from .krea_action_details import (
        dedupe_anchor_detail,
        dedupe_oral_detail,
        dedupe_outercourse_detail,
        dedupe_penetration_detail,
        dedupe_toy_double_detail,
        hardcore_item_detail,
        sanitize_foreplay_detail,
    )
    from .krea_action_climax import climax_role_graph, dedupe_climax_detail
except ImportError:  # Allows local smoke tests with `python -c`.
    from krea_action_context import (
        axis_values_text,
        is_climax_text,
        is_toy_assisted_double_text,
        normalize_hardcore_detail_density,
    )
    from hardcore_action_metadata import (
        ACTION_ANAL,
        ACTION_CLIMAX,
        ACTION_FOREPLAY,
        ACTION_MANUAL,
        ACTION_ORAL,
        ACTION_OUTERCOURSE,
        ACTION_PENETRATION,
        ACTION_TOY_DOUBLE,
        infer_hardcore_action_family,
        normalize_hardcore_action_family,
    )
    from krea_detail import limit_detail_for_density
    from krea_action_positions import hardcore_pose_anchor
    from krea_action_details import (
        dedupe_anchor_detail,
        dedupe_oral_detail,
        dedupe_outercourse_detail,
        dedupe_penetration_detail,
        dedupe_toy_double_detail,
        hardcore_item_detail,
        sanitize_foreplay_detail,
    )
    from krea_action_climax import climax_role_graph, dedupe_climax_detail


@dataclass(frozen=True)
class HardcoreActionParts:
    family: str
    role_graph: str
    hard_item: str
    detail: str
    anchor: str
    detail_density: str


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_hardcore_role_graph(role_graph: str) -> str:
    role_graph = _clean(role_graph).rstrip(".")
    replacements = (
        (
            r"\bthe man penetrates the woman while a toy adds a second point of contact\b",
            "the man's penis thrusts into the woman while a toy is positioned at the second penetration point",
        ),
        (
            r"\bthe man thrusts his penis into the woman while a toy adds a second penetration point\b",
            "the man's penis thrusts into the woman while a toy is positioned at the second penetration point",
        ),
        (
            r"\bthe man thrusts his penis into the woman\b",
            "the man's penis thrusts into the woman",
        ),
        (
            r"\bthe man penetrates the woman anally\b",
            "the man's penis thrusts into the woman's ass",
        ),
        (
            r"\bthe man thrusts his penis into the woman's ass\b",
            "the man's penis thrusts into the woman's ass",
        ),
        (
            r"\bthe man penetrates the woman\b",
            "the man's penis thrusts into the woman",
        ),
        (
            r"\bthe woman and the man are in mutual oral contact with mouth-to-genital contact visible\b",
            "the woman has the man's penis in her mouth while the man uses his mouth on her pussy",
        ),
        (
            r"\bthe woman gives oral to the man\b",
            "the woman takes the man's penis in her mouth",
        ),
    )
    for pattern, replacement in replacements:
        role_graph = re.sub(pattern, replacement, role_graph, flags=re.IGNORECASE)
    return role_graph


def normalize_toy_double_role_graph(role_graph: str) -> str:
    return re.sub(
        r"\s+while a toy adds (?:the|a) second penetration point\b",
        " while a toy is positioned at the second penetration point",
        role_graph,
        flags=re.IGNORECASE,
    )


def action_detail_for_family(
    family: str,
    detail: str,
    role_graph: str,
    hard_item: str,
    composition: str = "",
    axis_values: Any = None,
    *,
    anchor: str = "",
    detail_density: str = "balanced",
) -> tuple[str, str]:
    if family == ACTION_CLIMAX:
        return "", dedupe_climax_detail(detail, role_graph, detail_density)
    if family in (ACTION_FOREPLAY, ACTION_MANUAL):
        detail = sanitize_foreplay_detail(detail, role_graph, composition)
        return "", limit_detail_for_density(detail, detail_density, False)
    if family == ACTION_OUTERCOURSE:
        detail = dedupe_outercourse_detail(detail, role_graph, hard_item, axis_values)
        return "", limit_detail_for_density(detail, detail_density, False)
    if family == ACTION_ORAL and role_graph:
        detail = dedupe_oral_detail(detail, role_graph, hard_item, axis_values)
        return "", limit_detail_for_density(detail, detail_density, False)
    if family in (ACTION_ANAL, ACTION_PENETRATION) and role_graph:
        detail = dedupe_penetration_detail(detail, role_graph, hard_item, axis_values)
        return "", limit_detail_for_density(detail, detail_density, False)

    if anchor:
        detail = dedupe_anchor_detail(detail, anchor)
    if family == ACTION_TOY_DOUBLE:
        detail = dedupe_toy_double_detail(detail)
    return anchor, limit_detail_for_density(detail, detail_density, False)


def resolve_hardcore_action_parts(
    role_graph: str,
    hard_item: str,
    composition: str = "",
    axis_values: Any = None,
    detail_density: str = "balanced",
    action_family: Any = "",
) -> HardcoreActionParts:
    detail_density = normalize_hardcore_detail_density(detail_density)
    role_graph = normalize_hardcore_role_graph(role_graph)
    hard_item = _clean(hard_item).rstrip(".")
    axis_text = axis_values_text(axis_values)
    forced_family = normalize_hardcore_action_family(action_family)
    is_climax = forced_family == ACTION_CLIMAX or is_climax_text(role_graph, hard_item, composition, axis_text)
    if is_climax:
        role_graph = climax_role_graph(role_graph, hard_item, axis_values)

    detail = hardcore_item_detail(hard_item)
    anchor = hardcore_pose_anchor(role_graph, hard_item, composition, axis_values)
    family = forced_family or infer_hardcore_action_family(role_graph, hard_item, composition, axis_values, is_climax=is_climax)

    if is_toy_assisted_double_text(role_graph, hard_item, composition, axis_text):
        role_graph = normalize_toy_double_role_graph(role_graph)

    anchor, detail = action_detail_for_family(
        family,
        detail,
        role_graph,
        hard_item,
        composition,
        axis_values,
        anchor=anchor,
        detail_density=detail_density,
    )
    return HardcoreActionParts(
        family=family,
        role_graph=role_graph,
        hard_item=hard_item,
        detail=detail,
        anchor=anchor,
        detail_density=detail_density,
    )
