"""Tests for domain detection, playbook loading, and prompt assembly."""

from pathlib import Path
from unittest.mock import patch

import pytest

from fliiq.runtime.planning.domain_detector import detect_domain
from fliiq.runtime.planning.playbook_loader import load_playbook, load_soul
from fliiq.runtime.planning.prompt_assembly import assemble_system_prompt
from fliiq.runtime.agent.prompt import assemble_agent_prompt


# --- Domain Detector ---


def test_coding_detected():
    domains = detect_domain("build a Flask API with database endpoints")
    assert "coding" in domains


def test_single_keyword_below_threshold():
    domains = detect_domain("build a sandcastle")
    assert "coding" not in domains


def test_no_domain_for_generic_prompt():
    domains = detect_domain("what's the weather in Tokyo")
    assert domains == []


def test_case_insensitive():
    domains = detect_domain("BUILD a CODE module")
    assert "coding" in domains


def test_file_type_boost(tmp_path):
    (tmp_path / "app.py").touch()
    # "build" alone = 1, + file boost = 2 → threshold met
    domains = detect_domain("build something", working_dir=tmp_path)
    assert "coding" in domains


def test_no_file_boost_without_dir():
    # "build" alone = 1, no dir boost → below threshold
    domains = detect_domain("build something")
    assert "coding" not in domains


# --- Playbook Loader ---


def test_load_soul(tmp_path):
    (tmp_path / "SOUL.md").write_text("I am Fliiq.")
    soul, user_soul = load_soul(project_root=tmp_path)
    assert soul == "I am Fliiq."
    assert user_soul is None


def test_load_soul_with_user_override(tmp_path):
    (tmp_path / "SOUL.md").write_text("default")
    user_dir = tmp_path / ".fliiq"
    user_dir.mkdir()
    (user_dir / "SOUL.md").write_text("custom")
    soul, user_soul = load_soul(project_root=tmp_path)
    assert soul == "default"
    assert user_soul == "custom"


def test_load_soul_falls_back_to_bundled(tmp_path):
    """When project root has no SOUL.md, bundled SOUL.md is used."""
    soul, user_soul = load_soul(project_root=tmp_path)
    assert len(soul) > 0
    assert user_soul is None


def test_load_playbook(tmp_path):
    pb_dir = tmp_path / "playbooks"
    pb_dir.mkdir()
    (pb_dir / "coding.md").write_text("# Coding standards")
    result = load_playbook("coding", project_root=tmp_path)
    assert result == "# Coding standards"


def test_load_playbook_user_override(tmp_path):
    # Default
    pb_dir = tmp_path / "playbooks"
    pb_dir.mkdir()
    (pb_dir / "coding.md").write_text("default")
    # User override
    user_pb_dir = tmp_path / ".fliiq" / "playbooks"
    user_pb_dir.mkdir(parents=True)
    (user_pb_dir / "coding.md").write_text("custom")

    result = load_playbook("coding", project_root=tmp_path)
    assert result == "custom"


def test_load_playbook_not_found(tmp_path):
    result = load_playbook("nonexistent", project_root=tmp_path)
    assert result is None


# --- Prompt Assembly ---


def test_assemble_basic():
    result = assemble_system_prompt("You are Fliiq.")
    assert "You are Fliiq." in result


def test_assemble_with_user_soul():
    result = assemble_system_prompt("default", user_soul="Be extra cautious.")
    assert "default" in result
    assert "Be extra cautious." in result
    assert "User Overrides" in result


def test_assemble_with_playbook():
    result = assemble_system_prompt("soul", playbook="# Coding\nUse tests.")
    assert "Domain Playbook" in result
    assert "Use tests." in result


def test_assemble_with_skills():
    skills = [
        {"name": "web_fetch", "description": "Fetch HTML from URL"},
        {"name": "get_time", "description": "Get current time"},
    ]
    result = assemble_system_prompt("soul", skills=skills)
    assert "Available Skills" in result
    assert "web_fetch" in result
    assert "get_time" in result


def test_assemble_order():
    """Verify composition order: soul → user → playbook → skills."""
    result = assemble_system_prompt(
        "SOUL_HERE",
        user_soul="USER_HERE",
        playbook="PLAYBOOK_HERE",
        skills=[{"name": "SKILL_HERE", "description": "desc"}],
    )
    soul_pos = result.index("SOUL_HERE")
    user_pos = result.index("USER_HERE")
    playbook_pos = result.index("PLAYBOOK_HERE")
    skill_pos = result.index("SKILL_HERE")
    assert soul_pos < user_pos < playbook_pos < skill_pos


# --- Agent Prompt (runtime/agent/prompt.py) ---


def test_verification_instruction_in_agent_prompt():
    """Verification instruction should appear in assembled agent prompt."""
    result = assemble_agent_prompt("You are Fliiq.", mode="autonomous")
    assert "## Verification" in result
    assert "Do not skip this step" in result


def test_verification_instruction_ordering():
    """Verification instruction appears after decomposition, before playbook."""
    result = assemble_agent_prompt(
        "SOUL",
        playbook="PLAYBOOK_HERE",
        mode="autonomous",
    )
    decomp_pos = result.index("## Task Approach")
    verif_pos = result.index("## Verification")
    playbook_pos = result.index("PLAYBOOK_HERE")
    assert decomp_pos < verif_pos < playbook_pos


def test_fliiq_identity_section():
    """Fliiq Identity section appears when fliiq_email is provided."""
    result = assemble_agent_prompt("SOUL", fliiq_email="bot@fliiq.com")
    assert "## Fliiq Identity" in result
    assert "bot@fliiq.com" in result
    assert "YOUR inbox" in result


def test_fliiq_identity_absent_when_no_email():
    """Fliiq Identity section does not appear when fliiq_email is None."""
    result = assemble_agent_prompt("SOUL")
    assert "## Fliiq Identity" not in result


def test_fliiq_identity_before_user_profile():
    """Fliiq Identity appears before User Profile in prompt."""
    profile = {"name": "Andy", "emails": [{"address": "andy@gmail.com", "label": "personal"}]}
    result = assemble_agent_prompt("SOUL", fliiq_email="bot@fliiq.com", user_profile=profile)
    identity_pos = result.index("## Fliiq Identity")
    profile_pos = result.index("## User Profile")
    assert identity_pos < profile_pos
