from __future__ import annotations

import re
from typing import Any

try:
    from .krea_action_context import (
        axis_values_text,
        is_climax_text,
        is_foreplay_text,
        is_oral_text,
        is_outercourse_text,
        is_toy_assisted_double_text,
        is_vaginal_penetration_text,
    )
except ImportError:  # Allows local smoke tests with `python -c`.
    from krea_action_context import (
        axis_values_text,
        is_climax_text,
        is_foreplay_text,
        is_oral_text,
        is_outercourse_text,
        is_toy_assisted_double_text,
        is_vaginal_penetration_text,
    )


ACTION_CLIMAX = "climax"
ACTION_ANAL = "anal"
ACTION_FOREPLAY = "foreplay"
ACTION_MANUAL = "manual"
ACTION_OUTERCOURSE = "outercourse"
ACTION_ORAL = "oral"
ACTION_PENETRATION = "penetration"
ACTION_THREESOME = "threesome"
ACTION_GROUP = "group"
ACTION_TOY_DOUBLE = "toy_double"
ACTION_DEFAULT = "default"

HARDCORE_ACTION_FAMILY_CHOICES = {
    ACTION_CLIMAX,
    ACTION_ANAL,
    ACTION_FOREPLAY,
    ACTION_MANUAL,
    ACTION_OUTERCOURSE,
    ACTION_ORAL,
    ACTION_PENETRATION,
    ACTION_THREESOME,
    ACTION_GROUP,
    ACTION_TOY_DOUBLE,
    ACTION_DEFAULT,
}


def normalize_hardcore_action_family(value: Any, default: str = "") -> str:
    text = re.sub(r"[^a-z0-9]+", "_", str(value or "").strip().lower()).strip("_")
    aliases = {
        "penetrative": ACTION_PENETRATION,
        "penetrative_sex": ACTION_PENETRATION,
        "penetration_sex": ACTION_PENETRATION,
        "vaginal": ACTION_PENETRATION,
        "vaginal_penetration": ACTION_PENETRATION,
        "double": ACTION_TOY_DOUBLE,
        "double_penetration": ACTION_TOY_DOUBLE,
        "toy_double_penetration": ACTION_TOY_DOUBLE,
        "toy_assisted_double": ACTION_TOY_DOUBLE,
        "toy_assisted_double_penetration": ACTION_TOY_DOUBLE,
        "anal": ACTION_ANAL,
        "anal_sex": ACTION_ANAL,
        "anal_penetration": ACTION_ANAL,
        "outer_course": ACTION_OUTERCOURSE,
        "outercourse_sex": ACTION_OUTERCOURSE,
        "three_person": ACTION_THREESOME,
        "three_person_action": ACTION_THREESOME,
        "threesome": ACTION_THREESOME,
        "threesomes": ACTION_THREESOME,
        "threeway": ACTION_THREESOME,
        "three_way": ACTION_THREESOME,
        "group": ACTION_GROUP,
        "group_sex": ACTION_GROUP,
        "group_sex_orgy": ACTION_GROUP,
        "orgy": ACTION_GROUP,
        "manual": ACTION_MANUAL,
        "manual_stimulation": ACTION_MANUAL,
        "interaction": ACTION_FOREPLAY,
        "body_worship": ACTION_FOREPLAY,
        "body_worship_touching": ACTION_FOREPLAY,
        "foreplay_teasing": ACTION_FOREPLAY,
        "cumshot": ACTION_CLIMAX,
        "cumshot_climax": ACTION_CLIMAX,
        "orgasm_aftermath": ACTION_CLIMAX,
        "oral_sex": ACTION_ORAL,
    }
    text = aliases.get(text, text)
    return text if text in HARDCORE_ACTION_FAMILY_CHOICES else default


def infer_hardcore_action_family(
    role_graph: str,
    hard_item: str,
    composition: str = "",
    axis_values: Any = None,
    *,
    is_climax: bool | None = None,
) -> str:
    axis_text = axis_values_text(axis_values)
    if is_climax is None:
        is_climax = is_climax_text(role_graph, hard_item, composition, axis_text)
    if is_climax:
        return ACTION_CLIMAX
    if is_foreplay_text(role_graph, hard_item, composition, axis_text):
        return ACTION_FOREPLAY
    if is_outercourse_text(role_graph, hard_item, composition, axis_text):
        return ACTION_OUTERCOURSE
    if is_oral_text(role_graph, hard_item, composition, axis_text):
        return ACTION_ORAL
    if is_vaginal_penetration_text(role_graph, hard_item, composition, axis_text):
        return ACTION_PENETRATION
    if is_toy_assisted_double_text(role_graph, hard_item, composition, axis_text):
        return ACTION_TOY_DOUBLE
    return ACTION_DEFAULT


def source_hardcore_action_family(
    source_family: Any,
    role_graph: str,
    hard_item: str,
    composition: str = "",
    axis_values: Any = None,
) -> str:
    inferred = infer_hardcore_action_family(role_graph, hard_item, composition, axis_values)
    if inferred in (ACTION_CLIMAX, ACTION_TOY_DOUBLE):
        return inferred
    family = re.sub(r"[^a-z0-9]+", "_", str(source_family or "").strip().lower()).strip("_")
    family = {
        "penetration": "penetrative",
        "penetrative_sex": "penetrative",
        "outer_course": "outercourse",
        "outercourse_sex": "outercourse",
        "manual_stimulation": "manual",
        "foreplay_teasing": "foreplay",
        "body_worship": "interaction",
        "body_worship_touching": "interaction",
        "clothing_position_transitions": "interaction",
        "dominant_guidance": "interaction",
        "camera_performance": "interaction",
        "group_coordination": "interaction",
        "cumshot": "climax",
        "cumshot_climax": "climax",
        "oral_sex": "oral",
    }.get(family, family)
    source_mapping = {
        "penetrative": ACTION_PENETRATION,
        "anal": ACTION_ANAL,
        "foreplay": ACTION_FOREPLAY,
        "interaction": ACTION_FOREPLAY,
        "manual": ACTION_MANUAL,
        "oral": ACTION_ORAL,
        "outercourse": ACTION_OUTERCOURSE,
        "threesome": ACTION_THREESOME,
        "group": ACTION_GROUP,
        "climax": ACTION_CLIMAX,
    }
    return source_mapping.get(family, inferred)
