from __future__ import annotations

from dataclasses import dataclass
from typing import Any, Callable


CameraConfigWithMode = Callable[[str | dict[str, Any] | None, str], dict[str, Any]]
CameraDirective = Callable[[str | dict[str, Any] | None], tuple[str, dict[str, Any]]]
ApplyComposition = Callable[[dict[str, Any], str], dict[str, Any]]
CompositionPrompt = Callable[[Any, Any, str], str]
CameraSceneDirective = Callable[
    [Any, Any, str | dict[str, Any] | None, list[str] | None, str],
    tuple[str, dict[str, Any]],
]


def camera_config_with_detail(
    camera_config: dict[str, Any],
    camera_detail: str,
    camera_detail_choices: list[str] | tuple[str, ...],
) -> dict[str, Any]:
    if camera_detail in camera_detail_choices:
        camera_config["camera_detail"] = camera_detail
    return camera_config


def softcore_subject_kind(softcore_cast: Any, hard_women_count: int, hard_men_count: int) -> str:
    if str(softcore_cast) == "solo":
        return "woman"
    total = int(hard_women_count or 0) + int(hard_men_count or 0)
    if total == 2:
        return "couple"
    return "subjects"


def hardcore_subject_kind(hard_women_count: int, hard_men_count: int) -> str:
    total = int(hard_women_count or 0) + int(hard_men_count or 0)
    if total == 2:
        return "couple"
    return "subjects"


@dataclass(frozen=True)
class InstaPairCameraRoute:
    soft_row: dict[str, Any]
    hard_row: dict[str, Any]
    hard_scene: str
    hard_composition: str
    soft_camera_config: dict[str, Any]
    hard_camera_config: dict[str, Any]
    soft_camera_directive: str
    hard_camera_directive: str
    soft_camera_scene_directive: str
    hard_camera_scene_directive: str
    soft_camera_scene_sentence: str
    hard_camera_scene_sentence: str
    soft_camera_sentence: str
    hard_camera_sentence: str

    def as_dict(self) -> dict[str, Any]:
        return {
            "soft_row": self.soft_row,
            "hard_row": self.hard_row,
            "hard_scene": self.hard_scene,
            "hard_composition": self.hard_composition,
            "soft_camera_config": dict(self.soft_camera_config),
            "hard_camera_config": dict(self.hard_camera_config),
            "soft_camera_directive": self.soft_camera_directive,
            "hard_camera_directive": self.hard_camera_directive,
            "soft_camera_scene_directive": self.soft_camera_scene_directive,
            "hard_camera_scene_directive": self.hard_camera_scene_directive,
            "soft_camera_scene_sentence": self.soft_camera_scene_sentence,
            "hard_camera_scene_sentence": self.hard_camera_scene_sentence,
            "soft_camera_sentence": self.soft_camera_sentence,
            "hard_camera_sentence": self.hard_camera_sentence,
        }


def resolve_insta_pair_camera_result(
    *,
    soft_row: dict[str, Any],
    hard_row: dict[str, Any],
    options: dict[str, Any],
    camera_config: str | dict[str, Any] | None,
    softcore_camera_config: str | dict[str, Any] | None,
    hardcore_camera_config: str | dict[str, Any] | None,
    hard_women_count: int,
    hard_men_count: int,
    pov_character_labels: list[str],
    camera_detail_choices: list[str] | tuple[str, ...],
    camera_config_with_mode: CameraConfigWithMode,
    camera_directive: CameraDirective,
    apply_contextual_composition: ApplyComposition,
    contextual_composition_prompt: CompositionPrompt,
    composition_prompt: Callable[[Any], str],
    camera_scene_directive_for_context: CameraSceneDirective,
) -> InstaPairCameraRoute:
    hard_camera_mode = str(options["hardcore_camera_mode"])
    soft_camera_source = softcore_camera_config or camera_config
    hard_camera_source = hardcore_camera_config or camera_config
    if hard_camera_mode == "same_as_softcore":
        hard_camera_mode = str(options["softcore_camera_mode"])
        hard_camera_source = soft_camera_source

    soft_camera_config_dict = camera_config_with_mode(soft_camera_source, str(options["softcore_camera_mode"]))
    hard_camera_config_dict = camera_config_with_mode(hard_camera_source, hard_camera_mode)
    soft_camera_config_dict = camera_config_with_detail(
        soft_camera_config_dict,
        str(options["camera_detail"]),
        camera_detail_choices,
    )
    hard_camera_config_dict = camera_config_with_detail(
        hard_camera_config_dict,
        str(options["camera_detail"]),
        camera_detail_choices,
    )
    soft_camera_directive, soft_camera_config_dict = camera_directive(soft_camera_config_dict)
    hard_camera_directive, hard_camera_config_dict = camera_directive(hard_camera_config_dict)

    soft_subject_kind = softcore_subject_kind(options["softcore_cast"], hard_women_count, hard_men_count)
    hard_subject_kind = hardcore_subject_kind(hard_women_count, hard_men_count)
    soft_row = apply_contextual_composition(soft_row, soft_subject_kind)
    hard_row = apply_contextual_composition(hard_row, hard_subject_kind)

    hard_scene = soft_row["scene_text"] if options["continuity"] == "same_creator_same_room" else hard_row["scene_text"]
    if hard_scene != hard_row.get("scene_text"):
        hard_row["source_scene_text"] = hard_row.get("source_scene_text") or hard_row.get("scene_text", "")
        hard_row["scene_text"] = hard_scene

    hard_composition = contextual_composition_prompt(hard_scene, hard_row["composition"], hard_subject_kind)
    if hard_composition != hard_row["composition"]:
        hard_row["source_composition"] = hard_row.get("source_composition") or hard_row["composition"]
        hard_row["composition"] = hard_composition
        hard_row["composition_prompt"] = composition_prompt(hard_composition)

    soft_pov_camera_labels = pov_character_labels if options["softcore_cast"] == "same_as_hardcore" else []
    soft_camera_scene_directive, soft_camera_config_dict = camera_scene_directive_for_context(
        soft_row.get("scene_text"),
        soft_row.get("composition"),
        soft_camera_config_dict,
        soft_pov_camera_labels,
        soft_subject_kind,
    )
    hard_camera_scene_directive, hard_camera_config_dict = camera_scene_directive_for_context(
        hard_scene,
        hard_composition,
        hard_camera_config_dict,
        pov_character_labels,
        hard_subject_kind,
    )

    if soft_pov_camera_labels:
        soft_camera_directive = ""
    if pov_character_labels:
        hard_camera_directive = ""

    soft_row["camera_config"] = soft_camera_config_dict
    soft_row["camera_directive"] = soft_camera_directive
    soft_row["camera_scene_directive"] = soft_camera_scene_directive
    hard_row["camera_config"] = hard_camera_config_dict
    hard_row["camera_directive"] = hard_camera_directive
    hard_row["camera_scene_directive"] = hard_camera_scene_directive

    return InstaPairCameraRoute(
        soft_row=soft_row,
        hard_row=hard_row,
        hard_scene=hard_scene,
        hard_composition=hard_composition,
        soft_camera_config=soft_camera_config_dict,
        hard_camera_config=hard_camera_config_dict,
        soft_camera_directive=soft_camera_directive,
        hard_camera_directive=hard_camera_directive,
        soft_camera_scene_directive=soft_camera_scene_directive,
        hard_camera_scene_directive=hard_camera_scene_directive,
        soft_camera_scene_sentence=f"{soft_camera_scene_directive} " if soft_camera_scene_directive else "",
        hard_camera_scene_sentence=f"{hard_camera_scene_directive} " if hard_camera_scene_directive else "",
        soft_camera_sentence=f"Camera control: {soft_camera_directive} " if soft_camera_directive else "",
        hard_camera_sentence=f"Camera control: {hard_camera_directive} " if hard_camera_directive else "",
    )


def resolve_insta_pair_camera(
    *,
    soft_row: dict[str, Any],
    hard_row: dict[str, Any],
    options: dict[str, Any],
    camera_config: str | dict[str, Any] | None,
    softcore_camera_config: str | dict[str, Any] | None,
    hardcore_camera_config: str | dict[str, Any] | None,
    hard_women_count: int,
    hard_men_count: int,
    pov_character_labels: list[str],
    camera_detail_choices: list[str] | tuple[str, ...],
    camera_config_with_mode: CameraConfigWithMode,
    camera_directive: CameraDirective,
    apply_contextual_composition: ApplyComposition,
    contextual_composition_prompt: CompositionPrompt,
    composition_prompt: Callable[[Any], str],
    camera_scene_directive_for_context: CameraSceneDirective,
) -> dict[str, Any]:
    return resolve_insta_pair_camera_result(
        soft_row=soft_row,
        hard_row=hard_row,
        options=options,
        camera_config=camera_config,
        softcore_camera_config=softcore_camera_config,
        hardcore_camera_config=hardcore_camera_config,
        hard_women_count=hard_women_count,
        hard_men_count=hard_men_count,
        pov_character_labels=pov_character_labels,
        camera_detail_choices=camera_detail_choices,
        camera_config_with_mode=camera_config_with_mode,
        camera_directive=camera_directive,
        apply_contextual_composition=apply_contextual_composition,
        contextual_composition_prompt=contextual_composition_prompt,
        composition_prompt=composition_prompt,
        camera_scene_directive_for_context=camera_scene_directive_for_context,
    ).as_dict()
