from __future__ import annotations

import json
from typing import Any


STYLE_CONFIG_SCHEMA = "sxcp_style_config_v1"
STYLE_COMBINE_MODES = ("replace", "add", "disabled")
STYLE_PRESETS = {
    "category_default": {
        "style": "",
        "positive_suffix": "",
        "summary": "category default style",
    },
    "realistic_photo": {
        "style": "realistic adult photographic scene, natural camera capture",
        "positive_suffix": "Use realistic skin texture, natural light response, coherent anatomy, readable contact points, and believable spatial depth.",
        "summary": "realistic photographic style",
    },
    "creator_phone_photo": {
        "style": "realistic creator-shot phone photo, natural adult social-media image",
        "positive_suffix": "Use handheld camera realism, natural skin texture, readable body positioning, and believable room depth.",
        "summary": "creator phone photo style",
    },
    "documentary_flash": {
        "style": "realistic direct-flash documentary photo, raw adult snapshot",
        "positive_suffix": "Use direct flash, natural skin texture, sharp foreground detail, visible contact points, and unpolished camera realism.",
        "summary": "direct flash documentary style",
    },
    "cinematic_realism": {
        "style": "cinematic realistic adult scene, natural lens perspective",
        "positive_suffix": "Use realistic anatomy, readable blocking, natural depth, motivated lighting, and coherent camera perspective.",
        "summary": "cinematic realism style",
    },
    "comic_pinup_colored_pencil": {
        "style": "adult erotic coloured-pencil comic pin-up style",
        "positive_suffix": "Use crisp comic linework, detailed hatching, warm erotic lighting, soft skin shading, and tactile textured paper.",
        "summary": "colored-pencil comic pin-up style",
    },
    "flat_vector_comic": {
        "style": "flat vector adult comic illustration",
        "positive_suffix": "Use flat color, clean graphic shapes, crisp outlines, simplified shadows, and readable adult body positioning.",
        "summary": "flat vector comic style",
    },
}


def style_pool_preset_choices() -> list[str]:
    return list(STYLE_PRESETS)


def style_combine_mode_choices() -> list[str]:
    return list(STYLE_COMBINE_MODES)


def _clean_text(value: Any) -> str:
    return str(value or "").strip()


def _join_text(*values: Any) -> str:
    parts: list[str] = []
    for value in values:
        text = _clean_text(value)
        if text and text not in parts:
            parts.append(text.rstrip("."))
    return ". ".join(parts)


def parse_style_config(style_config: str | dict[str, Any] | None) -> dict[str, Any]:
    if not style_config:
        return {"enabled": False, "combine_mode": "disabled", "style": "", "positive_suffix": "", "negative_prompt": ""}
    if isinstance(style_config, dict):
        raw = dict(style_config)
    else:
        try:
            raw = json.loads(str(style_config))
        except json.JSONDecodeError:
            return {"enabled": False, "combine_mode": "disabled", "style": "", "positive_suffix": "", "negative_prompt": ""}
    if raw.get("schema") != STYLE_CONFIG_SCHEMA:
        return {"enabled": False, "combine_mode": "disabled", "style": "", "positive_suffix": "", "negative_prompt": ""}
    combine_mode = _clean_text(raw.get("combine_mode")) or "replace"
    if combine_mode not in STYLE_COMBINE_MODES:
        combine_mode = "replace"
    return {
        "schema": STYLE_CONFIG_SCHEMA,
        "version": 1,
        "enabled": bool(raw.get("enabled", True)) and combine_mode != "disabled",
        "combine_mode": combine_mode,
        "preset": _clean_text(raw.get("preset")) or "category_default",
        "style": _clean_text(raw.get("style")),
        "positive_suffix": _clean_text(raw.get("positive_suffix")),
        "negative_prompt": _clean_text(raw.get("negative_prompt")),
        "summary": _clean_text(raw.get("summary")) or "style config",
    }


def build_style_config_json(
    *,
    enabled: bool = True,
    combine_mode: str = "replace",
    preset: str = "category_default",
    custom_style: str = "",
    custom_positive_suffix: str = "",
    custom_negative: str = "",
    style_config: str | dict[str, Any] | None = "",
) -> str:
    if combine_mode not in STYLE_COMBINE_MODES:
        combine_mode = "replace"
    base = parse_style_config(style_config)
    preset_entry = STYLE_PRESETS.get(preset, STYLE_PRESETS["category_default"])
    style = _clean_text(custom_style) or preset_entry["style"]
    positive_suffix = _clean_text(custom_positive_suffix) or preset_entry["positive_suffix"]
    negative_prompt = _clean_text(custom_negative)
    if combine_mode == "add" and base.get("enabled"):
        style = _join_text(base.get("style"), style)
        positive_suffix = _join_text(base.get("positive_suffix"), positive_suffix)
        negative_prompt = _join_text(base.get("negative_prompt"), negative_prompt)
    payload = {
        "schema": STYLE_CONFIG_SCHEMA,
        "version": 1,
        "enabled": bool(enabled) and combine_mode != "disabled",
        "combine_mode": combine_mode,
        "preset": preset,
        "style": style,
        "positive_suffix": positive_suffix,
        "negative_prompt": negative_prompt,
        "summary": "style disabled" if not enabled or combine_mode == "disabled" else preset_entry["summary"],
    }
    return json.dumps(payload, ensure_ascii=True, sort_keys=True)


def resolve_style_fields(base_style: str, base_positive_suffix: str, style_config: str | dict[str, Any] | None) -> tuple[str, str]:
    config = parse_style_config(style_config)
    if not config.get("enabled"):
        return base_style, base_positive_suffix
    if config["combine_mode"] == "add":
        return (
            _join_text(base_style, config.get("style")),
            _join_text(base_positive_suffix, config.get("positive_suffix")),
        )
    return config.get("style", "") or base_style, config.get("positive_suffix", "") or base_positive_suffix


def merge_negative_prompt(base_negative: str, style_config: str | dict[str, Any] | None) -> str:
    config = parse_style_config(style_config)
    if not config.get("enabled"):
        return base_negative
    return _join_text(base_negative, config.get("negative_prompt"))
