"""Unit tests for layered Anima prompt assembly (no LLM)."""

import os
import sys
from unittest.mock import patch

import pytest

# Ensure project root on path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))

from services import sd_prompt as sp


@pytest.fixture
def anima():
    with patch.object(sp, "_is_anima", return_value=True), patch.object(sp, "_is_pony", return_value=False):
        yield


PERSONA_WOLF = {
    "appearance_tags": "wolfgirl, white_hair, golden_eyes, wolf_ears, tail, big_breast",
    "appearance_prose": "",
    "lora_name": "",
}

PERSONA_CARRIE = {
    "appearance_tags": "short_hair, brown_hair, blue_eyes, skinny",
    "appearance_prose": "",
    "lora_name": "",
}


def test_walking_scene_includes_action_tags_and_contextual_pov(anima):
    scene = sp._sanitize_scene_fields({
        "shot_type": "first_person_pov",
        "pov_cue": "walking_together",
        "viewer_body_visible": False,
        "action_tags": "holding_hands, walking, smiling, looking_at_each_other",
        "environment_tags": "outdoors, sunlight, golden_hour",
        "scene_description": "She walks beside you, laughter in the warm afternoon light.",
    })
    hybrid = sp.build_positive_prompt_hybrid(scene, PERSONA_WOLF, "")
    assert "walking" in hybrid
    assert "smiling" in hybrid
    assert "holding_hands" not in hybrid
    assert "looking_at_each_other" not in hybrid
    assert "outdoors" in hybrid
    assert "threshold" not in hybrid.lower()
    assert "POV: walking beside you" in hybrid
    assert "someone" not in hybrid.lower()
    assert "both " not in hybrid.lower()


def test_hybrid_differs_from_tags_only_when_prose_present(anima):
    scene = {
        "shot_type": "first_person_pov",
        "pov_cue": "walking_together",
        "viewer_body_visible": False,
        "action_tags": "holding_hands, walking",
        "environment_tags": "outdoors, sunlight",
        "scene_description": "Shared laughter drifts through the golden afternoon.",
    }
    tags_only = sp.build_positive_prompt_tags_only(scene, PERSONA_WOLF, "")
    hybrid = sp.build_positive_prompt_hybrid(scene, PERSONA_WOLF, "")
    assert tags_only != hybrid
    assert "Shared laughter" in hybrid
    assert "Shared laughter" not in tags_only


def test_carrie_doorway_scene(anima):
    scene = {
        "shot_type": "first_person_pov",
        "pov_cue": "doorway_invite",
        "viewer_body_visible": False,
        "action_tags": "arms_out, inviting_hug, smirk, looking_at_viewer",
        "environment_tags": "doorway, apartment, night, indoors",
        "scene_description": "She waits in the doorway with playful hunger in half-lidded eyes.",
    }
    outfit = "crop_top, ripped_jeans, black_jeans"
    hybrid = sp.build_positive_prompt_hybrid(scene, PERSONA_CARRIE, outfit)
    assert "arms_out" in hybrid
    assert "doorway" in hybrid
    assert "crop_top" in hybrid
    assert "threshold" not in hybrid.lower()
    assert "POV: she blocks the doorway" in hybrid


def test_pov_inferred_from_action_when_cue_missing(anima):
    scene = {
        "shot_type": "first_person_pov",
        "action_tags": "holding_hands, walking, smiling",
        "environment_tags": "outdoors, park",
        "scene_description": "",
    }
    tags = sp.build_positive_prompt_tags_only(scene, PERSONA_WOLF, "")
    assert "POV: walking beside you" in tags


def test_negative_includes_interaction_block_for_pov_contact(anima):
    scene = {
        "shot_type": "first_person_pov",
        "viewer_body_visible": False,
        "action_tags": "arms_out, hug, inviting_hug",
        "environment_tags": "doorway",
    }
    neg = sp._negative_for_scene(scene)
    assert "duplicate" in neg
    assert "extra_person" in neg
    assert "third person" in neg


def test_scene_should_generate_false():
    assert sp._scene_should_generate({"should_generate": False}) is False
    assert sp._scene_should_generate({"should_generate": True}) is True
    assert sp._scene_should_generate({}) is True


def test_format_builder_user_block_illustrate_vs_context(anima):
    messages = [
        {"role": "assistant", "content": "Long old first_mes " + ("x" * 900)},
        {"role": "user", "content": "Hi"},
        {"role": "assistant", "content": "*walks holding your hand*"},
    ]
    block = sp._format_builder_user_block(PERSONA_WOLF, messages, "[]")
    assert "=== ILLUSTRATE" in block
    assert "=== Context" in block
    assert "*walks holding your hand*" in block
    assert "Long old first_mes" in block
    assert len(block.split("Long old first_mes")[1].split("assistant:")[0]) < 900


def test_bundle_from_scene_anima_uses_hybrid_as_tag_full(anima):
    scene = {
        "should_generate": True,
        "shot_type": "first_person_pov",
        "pov_cue": "face_to_face",
        "action_tags": "smiling",
        "environment_tags": "indoors",
        "scene_description": "A warm smile greets you.",
    }
    with patch.object(sp, "anima_dual_enabled", return_value=False):
        bundle = sp._bundle_from_scene(scene, PERSONA_WOLF, "")
    assert "A warm smile" in bundle.tag_full
    assert bundle.desc_full is None


def test_user_example_walking_llm_output_cleaned(anima):
    """Regression: LLM prose/sentence leakage and second-person refs."""
    scene = sp._sanitize_scene_fields({
        "shot_type": "first_person_pov",
        "pov_cue": "walking_together",
        "action_tags": (
            "holding_hands, walking, smiling, looking_at_each_other, "
            "A wolfgirl walks hand in hand with someone, both smiling and chatting"
        ),
        "environment_tags": "outdoor, daylight, path",
        "scene_description": (
            "A wolfgirl walks hand in hand with someone, both smiling and chatting under the daylight."
        ),
    })
    persona = {**PERSONA_WOLF, "appearance_tags": PERSONA_WOLF["appearance_tags"] + ", pumped_up"}
    tags_only = sp.build_positive_prompt_tags_only(scene, persona, "")
    hybrid = sp.build_positive_prompt_hybrid(scene, persona, "")
    assert "pumped_up" not in tags_only
    assert "someone" not in hybrid.lower()
    assert "both " not in hybrid.lower()
    assert ". A wolfgirl walks" not in tags_only
    assert tags_only != hybrid or not scene.get("scene_description")


def test_user_example_carrie_env_reconciled(anima):
    scene = sp._sanitize_scene_fields({
        "shot_type": "first_person_pov",
        "pov_cue": "doorway_invite",
        "action_tags": "arms_out, inviting_hug, smirk, half-lidded_eyes",
        "environment_tags": "doorway, nighttime, outdoor",
        "scene_description": (
            "Carrie stands in her doorway at night, arms outstretched toward you with a mischievous smirk."
        ),
    })
    hybrid = sp.build_positive_prompt_hybrid(
        scene, PERSONA_CARRIE, "crop_top, ripped_jeans, black_jeans, jeans"
    )
    assert "outdoor" not in hybrid.lower() or "doorway" in hybrid
    assert ", jeans," not in f", {hybrid},"
    assert "someone" not in hybrid.lower()


def test_long_first_mes_uses_final_beat(anima):
    carrie_tail = (
        "About an hour later...\n\n"
        "Carrie stood at her front door, arms out, smirking. "
        '"Come on, hug me. Now." It\'s getting cold out.'
    )
    long = ("She shops for clothes.\n\n" * 5) + carrie_tail
    excerpt = sp._extract_illustrate_content(long)
    assert "front door" in excerpt or "hug me" in excerpt
    assert "shops for clothes" not in excerpt


def test_hybrid_gets_fallback_when_no_scene_description(anima):
    scene = sp._sanitize_scene_fields({
        "shot_type": "first_person_pov",
        "pov_cue": "walking_together",
        "action_tags": "walking, smiling",
        "environment_tags": "outdoor, daylight",
        "scene_description": "",
    })
    tags_only = sp.build_positive_prompt_tags_only(scene, PERSONA_WOLF, "")
    hybrid = sp.build_positive_prompt_hybrid(scene, PERSONA_WOLF, "")
    assert hybrid != tags_only
    assert "afternoon" in hybrid.lower() or "laughter" in hybrid.lower()


def test_yuki_pov_drops_lifting_and_nose_rub(anima):
    scene = sp._sanitize_scene_fields({
        "shot_type": "first_person_pov",
        "pov_cue": "face_to_face",
        "action_tags": "arms_out, lifting, nose_rub, smiling",
        "environment_tags": "indoors, warm_lighting",
        "scene_description": "Her golden eyes soften with warmth toward the camera.",
    })
    hybrid = sp.build_positive_prompt_hybrid(scene, {**PERSONA_WOLF, "appearance_tags": "fox_girl, golden_eyes"}, "pink_sweater")
    assert "lifting" not in hybrid
    assert "nose_rub" not in hybrid
    assert "golden" in hybrid.lower()


def test_bundle_tags_only_alt_when_dual_compare(anima):
    scene = {
        "shot_type": "first_person_pov",
        "pov_cue": "dialogue_close",
        "action_tags": "smiling",
        "environment_tags": "indoors",
        "scene_description": "Soft light on her face.",
    }
    with patch.object(sp, "anima_dual_enabled", return_value=True):
        bundle = sp._bundle_from_scene(scene, PERSONA_WOLF, "")
    assert bundle.desc_full is not None
    assert bundle.desc_full != bundle.tag_full
    assert "Soft light" in bundle.tag_full
    assert "Soft light" not in bundle.desc_full.split(sp.NEGATIVE_PROMPT_SEPARATOR)[0]
