"""Tests for participants_loader module."""

import os
import tempfile
from pathlib import Path

import pytest
import yaml

from thetable_poc.config.participants_loader import (
    ParticipantEntry,
    create_participants,
    load_participant_pool,
    apply_role_to_pool,
    load_participants,
)
from thetable_poc.core import Participant, Role


@pytest.fixture
def valid_yaml_content():
    """Valid YAML configuration."""
    return {
        "participants": [
            {"name": "Alex", "role": "host", "type": "ai", "checked": True},
            {"name": "Sarah", "role": "pm", "type": "ai", "checked": True},
            {"name": "John", "role": "tech_lead", "type": "ai", "checked": False},
            {"name": "Mr.Yong", "role": "member", "type": "human", "checked": True},
        ]
    }


@pytest.fixture
def valid_yaml_file(valid_yaml_content):
    """Create temporary valid YAML file."""
    with tempfile.NamedTemporaryFile(
        mode="w", suffix=".yaml", delete=False, encoding="utf-8"
    ) as f:
        yaml.dump(valid_yaml_content, f)
        temp_path = f.name
    yield temp_path
    os.unlink(temp_path)


def test_load_participant_pool_valid(valid_yaml_file):
    """Test loading valid participant pool."""
    entries = load_participant_pool(valid_yaml_file)

    assert len(entries) == 4
    assert entries[0].name == "Alex"
    assert entries[0].role == "host"
    assert entries[0].type == "ai"
    assert entries[0].checked is True


def test_load_participant_pool_missing_file():
    """Test loading non-existent file."""
    with pytest.raises(FileNotFoundError):
        load_participant_pool("/nonexistent/path.yaml")


def test_load_participant_pool_invalid_yaml():
    """Test loading invalid YAML (missing participants key)."""
    with tempfile.NamedTemporaryFile(
        mode="w", suffix=".yaml", delete=False, encoding="utf-8"
    ) as f:
        yaml.dump({"invalid": "data"}, f)
        temp_path = f.name

    try:
        with pytest.raises(ValueError, match="participants"):
            load_participant_pool(temp_path)
    finally:
        os.unlink(temp_path)


def test_load_participant_pool_missing_required_fields():
    """Test missing required fields."""
    content = {"participants": [{"name": "Alex"}]}  # Missing role and type

    with tempfile.NamedTemporaryFile(
        mode="w", suffix=".yaml", delete=False, encoding="utf-8"
    ) as f:
        yaml.dump(content, f)
        temp_path = f.name

    try:
        with pytest.raises(ValueError, match="missing 'role' field"):
            load_participant_pool(temp_path)
    finally:
        os.unlink(temp_path)


def test_load_participant_pool_invalid_role():
    """Test invalid role value."""
    content = {
        "participants": [
            {"name": "Alex", "role": "invalid_role", "type": "ai"},
            {"name": "Sarah", "role": "pm", "type": "ai"},
        ]
    }

    with tempfile.NamedTemporaryFile(
        mode="w", suffix=".yaml", delete=False, encoding="utf-8"
    ) as f:
        yaml.dump(content, f)
        temp_path = f.name

    try:
        with pytest.raises(ValueError, match="Invalid role"):
            load_participant_pool(temp_path)
    finally:
        os.unlink(temp_path)


def test_load_participant_pool_invalid_type():
    """Test invalid type value."""
    content = {
        "participants": [
            {"name": "Alex", "role": "host", "type": "robot"},
            {"name": "Sarah", "role": "pm", "type": "ai"},
        ]
    }

    with tempfile.NamedTemporaryFile(
        mode="w", suffix=".yaml", delete=False, encoding="utf-8"
    ) as f:
        yaml.dump(content, f)
        temp_path = f.name

    try:
        with pytest.raises(ValueError, match="Invalid type"):
            load_participant_pool(temp_path)
    finally:
        os.unlink(temp_path)


def test_load_participant_pool_duplicate_name():
    """Test duplicate participant names."""
    content = {
        "participants": [
            {"name": "Alex", "role": "host", "type": "ai"},
            {"name": "Alex", "role": "pm", "type": "ai"},
        ]
    }

    with tempfile.NamedTemporaryFile(
        mode="w", suffix=".yaml", delete=False, encoding="utf-8"
    ) as f:
        yaml.dump(content, f)
        temp_path = f.name

    try:
        with pytest.raises(ValueError, match="Duplicate participant name"):
            load_participant_pool(temp_path)
    finally:
        os.unlink(temp_path)


def test_load_participant_pool_missing_host():
    """Test missing HOST role."""
    content = {
        "participants": [
            {"name": "Sarah", "role": "pm", "type": "ai"},
            {"name": "John", "role": "tech_lead", "type": "ai"},
        ]
    }

    with tempfile.NamedTemporaryFile(
        mode="w", suffix=".yaml", delete=False, encoding="utf-8"
    ) as f:
        yaml.dump(content, f)
        temp_path = f.name

    try:
        with pytest.raises(ValueError, match="HOST 역할 참여자가 풀에 없습니다"):
            load_participant_pool(temp_path)
    finally:
        os.unlink(temp_path)


def test_load_participant_pool_missing_pm():
    """Test missing PM role."""
    content = {
        "participants": [
            {"name": "Alex", "role": "host", "type": "ai"},
            {"name": "John", "role": "tech_lead", "type": "ai"},
        ]
    }

    with tempfile.NamedTemporaryFile(
        mode="w", suffix=".yaml", delete=False, encoding="utf-8"
    ) as f:
        yaml.dump(content, f)
        temp_path = f.name

    try:
        with pytest.raises(ValueError, match="PM 역할 참여자가 풀에 없습니다"):
            load_participant_pool(temp_path)
    finally:
        os.unlink(temp_path)


def test_load_participant_pool_multiple_hosts():
    """Test multiple HOST roles."""
    content = {
        "participants": [
            {"name": "Alex", "role": "host", "type": "ai"},
            {"name": "Bob", "role": "host", "type": "ai"},
            {"name": "Sarah", "role": "pm", "type": "ai"},
        ]
    }

    with tempfile.NamedTemporaryFile(
        mode="w", suffix=".yaml", delete=False, encoding="utf-8"
    ) as f:
        yaml.dump(content, f)
        temp_path = f.name

    try:
        with pytest.raises(ValueError, match="HOST 역할은 1명만 가능합니다"):
            load_participant_pool(temp_path)
    finally:
        os.unlink(temp_path)


def test_load_participant_pool_with_mcp_tools():
    """Test loading with MCP tools."""
    content = {
        "participants": [
            {"name": "Alex", "role": "host", "type": "ai"},
            {
                "name": "Sarah",
                "role": "pm",
                "type": "ai",
                "mcp_tools": ["jira", "linear"],
            },
        ]
    }

    with tempfile.NamedTemporaryFile(
        mode="w", suffix=".yaml", delete=False, encoding="utf-8"
    ) as f:
        yaml.dump(content, f)
        temp_path = f.name

    try:
        entries = load_participant_pool(temp_path)
        assert entries[1].mcp_tools == ["jira", "linear"]
    finally:
        os.unlink(temp_path)


def test_create_participants():
    """Test creating Participant objects from entries."""
    entries = [
        ParticipantEntry(name="Alex", role="host", type="ai", checked=True),
        ParticipantEntry(name="Sarah", role="pm", type="ai", checked=True),
        ParticipantEntry(name="Mr.Yong", role="member", type="human", checked=True),
    ]

    participants_list, agents_map = create_participants(entries)

    # Check list
    assert len(participants_list) == 3
    assert all(isinstance(p, Participant) for p in participants_list)

    # Check map keys
    assert "host" in agents_map
    assert "pm" in agents_map
    assert "member" in agents_map

    # Check participant properties
    assert agents_map["host"].name == "Alex"
    assert agents_map["host"].role == Role.HOST
    assert agents_map["pm"].name == "Sarah"
    assert agents_map["member"].name == "Mr.Yong"
    assert agents_map["member"].is_human


def test_create_participants_duplicate_roles():
    """Test creating participants with duplicate roles."""
    entries = [
        ParticipantEntry(name="Alex", role="host", type="ai", checked=True),
        ParticipantEntry(name="Sarah", role="pm", type="ai", checked=True),
        ParticipantEntry(name="Bob", role="backend", type="ai", checked=True),
        ParticipantEntry(name="Alice", role="backend", type="ai", checked=True),
    ]

    participants_list, agents_map = create_participants(entries)

    # First backend uses role key
    assert "backend" in agents_map
    assert agents_map["backend"].name == "Bob"

    # Second backend uses name key
    assert "Alice" in agents_map
    assert agents_map["Alice"].name == "Alice"


def test_load_participant_pool_default_path():
    """Test loading from default config/participants.yaml path."""
    # Ensure no environment variable is set
    env_backup = os.environ.pop("PARTICIPANTS_CONFIG", None)

    try:
        entries = load_participant_pool()
        # Should load from config/participants.yaml (9 participants)
        assert len(entries) == 9
        assert any(e.name == "Alex" and e.role == "host" for e in entries)
        assert any(e.name == "Sarah" and e.role == "pm" for e in entries)
    finally:
        if env_backup:
            os.environ["PARTICIPANTS_CONFIG"] = env_backup


def test_apply_role_to_pool_member():
    """Test applying member role to human participant."""
    pool = [
        ParticipantEntry(name="Alex", role="host", type="ai", checked=True),
        ParticipantEntry(name="Sarah", role="pm", type="ai", checked=True),
        ParticipantEntry(name="Mr.Yong", role="member", type="human", checked=True),
    ]

    result = apply_role_to_pool(pool, "member")

    # Human role should be 'member'
    human_entry = next((e for e in result if e.type == "human"), None)
    assert human_entry is not None
    assert human_entry.role == "member"
    assert human_entry.name == "Mr.Yong"

    # Pool size should remain the same (no AI removal for member role)
    assert len(result) == 3


def test_apply_role_to_pool_pm_replaces_ai():
    """Test PM role selection removes AI PM."""
    pool = [
        ParticipantEntry(name="Alex", role="host", type="ai", checked=True),
        ParticipantEntry(name="Sarah", role="pm", type="ai", checked=True),
        ParticipantEntry(name="John", role="tech_lead", type="ai", checked=False),
        ParticipantEntry(name="Mr.Yong", role="member", type="human", checked=True),
    ]

    result = apply_role_to_pool(pool, "pm")

    # Human role should be 'pm'
    human_entry = next((e for e in result if e.type == "human"), None)
    assert human_entry is not None
    assert human_entry.role == "pm"
    assert human_entry.name == "Mr.Yong"

    # AI PM should be removed
    ai_pm_count = sum(1 for e in result if e.type == "ai" and e.role == "pm")
    assert ai_pm_count == 0

    # Pool should have 3 participants (AI PM removed)
    assert len(result) == 3


def test_apply_role_to_pool_tech_lead_replaces_ai():
    """Test tech_lead role selection removes AI tech_lead."""
    pool = [
        ParticipantEntry(name="Alex", role="host", type="ai", checked=True),
        ParticipantEntry(name="Sarah", role="pm", type="ai", checked=True),
        ParticipantEntry(name="John", role="tech_lead", type="ai", checked=False),
        ParticipantEntry(name="Mr.Yong", role="member", type="human", checked=True),
    ]

    result = apply_role_to_pool(pool, "tech_lead")

    # Human role should be 'tech_lead'
    human_entry = next((e for e in result if e.type == "human"), None)
    assert human_entry is not None
    assert human_entry.role == "tech_lead"
    assert human_entry.name == "Mr.Yong"

    # AI tech_lead should be removed
    ai_tech_lead_count = sum(1 for e in result if e.type == "ai" and e.role == "tech_lead")
    assert ai_tech_lead_count == 0

    # Pool should have 3 participants (AI tech_lead removed)
    assert len(result) == 3


def test_load_participants_with_human_role(valid_yaml_file):
    """Test full load_participants flow with human role selection."""
    # Test with PM role
    participants_list, agents_map = load_participants(
        valid_yaml_file,
        select_mode="all",
        human_role="pm"
    )

    # Should have 3 participants (AI PM removed, human PM added)
    assert len(participants_list) == 3

    # PM should be human
    pm_participant = agents_map.get("pm")
    assert pm_participant is not None
    assert pm_participant.is_human
    assert pm_participant.role == Role.PM

    # Should not have any AI PM
    ai_pm_count = sum(1 for p in participants_list if p.role == Role.PM and p.is_ai)
    assert ai_pm_count == 0
