from __future__ import annotations

import json
from typing import Any


GENERATION_PROFILE_PRESETS = {
    "balanced": {
        "clothing": "full",
        "poses": "standard",
        "expression_enabled": True,
        "expression_intensity": 0.5,
        "backside_bias": 0.0,
        "minimal_clothing_ratio": -1.0,
        "standard_pose_ratio": -1.0,
        "trigger": "sxcpinup_coloredpencil",
        "prepend_trigger_to_prompt": True,
    },
    "casual_clean": {
        "clothing": "full",
        "poses": "standard",
        "expression_enabled": True,
        "expression_intensity": 0.35,
        "backside_bias": 0.0,
        "minimal_clothing_ratio": -1.0,
        "standard_pose_ratio": -1.0,
        "trigger": "sxcpinup_coloredpencil",
        "prepend_trigger_to_prompt": True,
    },
    "evocative_softcore": {
        "clothing": "minimal",
        "poses": "evocative",
        "expression_enabled": True,
        "expression_intensity": 0.65,
        "backside_bias": 0.2,
        "minimal_clothing_ratio": -1.0,
        "standard_pose_ratio": -1.0,
        "trigger": "sxcpinup_coloredpencil",
        "prepend_trigger_to_prompt": True,
    },
    "hardcore_intense": {
        "clothing": "minimal",
        "poses": "evocative",
        "expression_enabled": True,
        "expression_intensity": 0.9,
        "backside_bias": 0.0,
        "minimal_clothing_ratio": -1.0,
        "standard_pose_ratio": -1.0,
        "trigger": "sxcpinup_coloredpencil",
        "prepend_trigger_to_prompt": True,
    },
    "krea2_friendly": {
        "clothing": "full",
        "poses": "standard",
        "expression_enabled": True,
        "expression_intensity": 0.55,
        "backside_bias": 0.0,
        "minimal_clothing_ratio": -1.0,
        "standard_pose_ratio": -1.0,
        "trigger": "sxcpinup_coloredpencil",
        "prepend_trigger_to_prompt": False,
    },
    "flux_original": {
        "clothing": "full",
        "poses": "standard",
        "expression_enabled": True,
        "expression_intensity": 0.5,
        "backside_bias": 0.0,
        "minimal_clothing_ratio": -1.0,
        "standard_pose_ratio": -1.0,
        "trigger": "sxcpinup_coloredpencil",
        "prepend_trigger_to_prompt": True,
    },
}


def _is_false(value: Any) -> bool:
    if isinstance(value, bool):
        return value is False
    if isinstance(value, str):
        return value.strip().lower() in ("false", "0", "no", "off")
    return False


def _clamped_float(value: Any, default: float = 0.5, min_value: float = 0.0, max_value: float = 1.0) -> float:
    try:
        number = float(value)
    except (TypeError, ValueError):
        return default
    return max(min_value, min(max_value, number))


def generation_profile_choices() -> list[str]:
    return list(GENERATION_PROFILE_PRESETS)


def build_generation_profile_json(
    profile: str = "balanced",
    clothing_override: str = "profile_default",
    poses_override: str = "profile_default",
    expression_intensity_mode: str = "profile_default",
    expression_intensity: float = -1.0,
    backside_bias: float = -1.0,
    minimal_clothing_ratio: float = -1.0,
    standard_pose_ratio: float = -1.0,
    trigger_policy: str = "profile_default",
    expression_enabled: bool = True,
) -> str:
    profile = profile if profile in GENERATION_PROFILE_PRESETS else "balanced"
    config = dict(GENERATION_PROFILE_PRESETS[profile])
    if clothing_override in ("full", "minimal", "random"):
        config["clothing"] = clothing_override
    if poses_override in ("standard", "evocative", "random"):
        config["poses"] = poses_override
    config["expression_enabled"] = not _is_false(expression_enabled)
    if expression_intensity_mode == "random":
        config["expression_intensity"] = -1.0
    elif expression_intensity_mode == "fixed" and float(expression_intensity) >= 0:
        config["expression_intensity"] = _clamped_float(expression_intensity, config["expression_intensity"])
    if float(backside_bias) >= 0:
        config["backside_bias"] = _clamped_float(backside_bias, config["backside_bias"])
    if float(minimal_clothing_ratio) >= 0:
        config["minimal_clothing_ratio"] = _clamped_float(minimal_clothing_ratio, config["minimal_clothing_ratio"])
    if float(standard_pose_ratio) >= 0:
        config["standard_pose_ratio"] = _clamped_float(standard_pose_ratio, config["standard_pose_ratio"])
    if trigger_policy == "prepend_trigger":
        config["prepend_trigger_to_prompt"] = True
    elif trigger_policy == "do_not_prepend":
        config["prepend_trigger_to_prompt"] = False
    config["profile"] = profile
    return json.dumps(config, ensure_ascii=True, sort_keys=True)


def parse_generation_profile(profile_config: str | dict[str, Any] | None) -> dict[str, Any]:
    if not profile_config:
        return dict(GENERATION_PROFILE_PRESETS["balanced"])
    if isinstance(profile_config, dict):
        raw = profile_config
    else:
        try:
            raw = json.loads(str(profile_config))
        except json.JSONDecodeError as exc:
            raise ValueError(f"Invalid generation_profile JSON: {exc}") from exc
    if not isinstance(raw, dict):
        raise ValueError("generation_profile must be a JSON object")
    profile = str(raw.get("profile") or "balanced")
    parsed = dict(GENERATION_PROFILE_PRESETS.get(profile, GENERATION_PROFILE_PRESETS["balanced"]))
    parsed.update(raw)
    parsed["clothing"] = parsed["clothing"] if parsed.get("clothing") in ("full", "minimal", "random") else "full"
    parsed["poses"] = parsed["poses"] if parsed.get("poses") in ("standard", "evocative", "random") else "standard"
    parsed["expression_enabled"] = not _is_false(parsed.get("expression_enabled", True))
    try:
        raw_expression_intensity = float(parsed.get("expression_intensity"))
    except (TypeError, ValueError):
        raw_expression_intensity = 0.5
    parsed["expression_intensity"] = -1.0 if raw_expression_intensity < 0 else _clamped_float(raw_expression_intensity, 0.5)
    parsed["backside_bias"] = _clamped_float(parsed.get("backside_bias"), 0.0)
    parsed["minimal_clothing_ratio"] = _clamped_float(parsed.get("minimal_clothing_ratio"), -1.0, -1.0, 1.0)
    parsed["standard_pose_ratio"] = _clamped_float(parsed.get("standard_pose_ratio"), -1.0, -1.0, 1.0)
    parsed["trigger"] = str(parsed.get("trigger") or "sxcpinup_coloredpencil")
    parsed["prepend_trigger_to_prompt"] = bool(parsed.get("prepend_trigger_to_prompt"))
    return parsed


_parse_generation_profile = parse_generation_profile
