from __future__ import annotations

import os

import pytest

from logos_adk.errors import ConfigError
from logos_adk.dynamic_orchestration import DepartmentSupervisor
from logos_adk.dynamic_orchestration_config_parser import (
    load_department_supervisor_config,
    load_department_supervisor_from_config,
    parse_department_supervisor_config_data,
)
from logos_adk.providers.mock import MockProvider
from logos_adk.types import Response, Usage


FIXTURES = os.path.join(os.path.dirname(__file__), "fixtures")


async def declarative_swarm_handler(event):
    return {"content": f"swarm-config::{event.payload['prompt'].upper()}"}


def _response(text: str) -> Response:
    return Response(content=text, tool_calls=[], usage=Usage(1, 1), raw={})


class TestParseDepartmentSupervisorConfigData:
    def test_parses_inline_department_supervisor(self):
        cfg = parse_department_supervisor_config_data(
            {
                "name": "ceo",
                "type": "department_supervisor",
                "router": {"name": "router", "model": "mock"},
                "departments": [
                    {
                        "name": "seo_team",
                        "kind": "team",
                        "team": {
                            "name": "seo",
                            "type": "team",
                            "agents": [{"name": "seo", "model": "mock"}],
                        },
                    },
                    {
                        "name": "seo_swarm",
                        "kind": "swarm",
                        "request_topic": "need_seo",
                        "response_topic": "seo_done",
                        "subscriptions": [
                            {
                                "topic": "need_seo",
                                "handler": "tests.test_dynamic_orchestration_config:declarative_swarm_handler",
                                "publish_to": "seo_done",
                            }
                        ],
                    },
                ],
            }
        )

        assert cfg.name == "ceo"
        assert cfg.router is not None
        assert len(cfg.departments) == 2
        assert cfg.departments[0].kind == "team"
        assert cfg.departments[1].request_topic == "need_seo"

    def test_rejects_unknown_fallback_department(self):
        with pytest.raises(ConfigError, match="fallback_department"):
            parse_department_supervisor_config_data(
                {
                    "name": "ceo",
                    "type": "department_supervisor",
                    "fallback_department": "missing",
                    "departments": [
                        {
                            "name": "seo_team",
                            "kind": "team",
                            "team": {
                                "name": "seo",
                                "type": "team",
                                "agents": [{"name": "seo", "model": "mock"}],
                            },
                        }
                    ],
                }
            )

    def test_parses_postgresql_event_bus_for_swarm_department(self):
        cfg = parse_department_supervisor_config_data(
            {
                "name": "ceo",
                "type": "department_supervisor",
                "departments": [
                    {
                        "name": "seo_swarm",
                        "kind": "swarm",
                        "request_topic": "need_seo",
                        "response_topic": "seo_done",
                        "event_bus": {
                            "backend": "postgresql",
                            "dsn": "postgresql://bus-db/logos",
                        },
                        "subscriptions": [
                            {
                                "topic": "need_seo",
                                "handler": "tests.test_dynamic_orchestration_config:declarative_swarm_handler",
                                "publish_to": "seo_done",
                            }
                        ],
                    }
                ],
            }
        )

        assert cfg.departments[0].event_bus is not None
        assert cfg.departments[0].event_bus.backend == "postgresql"
        assert cfg.departments[0].event_bus.dsn == "postgresql://bus-db/logos"


class TestLoadDepartmentSupervisorConfig:
    def test_load_fixture(self):
        cfg = load_department_supervisor_config(
            os.path.join(FIXTURES, "department_supervisor.yaml")
        )

        assert cfg.name == "ceo"
        assert cfg.router is not None
        assert cfg.fallback_department == "seo_team"
        assert len(cfg.departments) == 3
        assert cfg.departments[1].kind == "crew"
        assert cfg.departments[2].subscriptions[0].handler.endswith(
            "declarative_swarm_handler"
        )

    def test_parse_supports_team_path_reference(self):
        cfg = parse_department_supervisor_config_data(
            {
                "name": "ceo",
                "type": "department_supervisor",
                "departments": [
                    {
                        "name": "seo_team",
                        "kind": "team",
                        "team": "team_event_bus.yaml",
                    }
                ],
            },
            base_dir=FIXTURES,
        )

        assert cfg.departments[0].team is not None
        assert cfg.departments[0].team.name == "seo_team"

    @pytest.mark.asyncio
    async def test_load_department_supervisor_from_config_builds_runtime(self):
        supervisor = load_department_supervisor_from_config(
            os.path.join(FIXTURES, "department_supervisor.yaml")
        )

        assert isinstance(supervisor, DepartmentSupervisor)
        assert len(supervisor.list_departments()) == 3

        supervisor._router.model(
            MockProvider(
                responses=[
                    _response(
                        '{"department_name":"seo_team","delegated_prompt":"Fix listing SEO","reason":"seo"}'
                    )
                ]
            )
        )
        seo_department = next(
            item for item in supervisor.list_departments() if item.name == "seo_team"
        )
        seo_agent = seo_department.target.get_agent("seo")
        seo_agent.model(MockProvider(responses=[_response("team-config::ok")]))

        response = await supervisor.run("Need SEO help")

        assert response.content == "team-config::ok"
        assert response.raw["dynamic_orchestration"]["department_name"] == "seo_team"

    @pytest.mark.asyncio
    async def test_load_department_supervisor_from_config_supports_swarm(self):
        supervisor = load_department_supervisor_from_config(
            os.path.join(FIXTURES, "department_supervisor.yaml")
        )
        supervisor._router.model(
            MockProvider(
                responses=[
                    _response(
                        '{"department_name":"seo_swarm","delegated_prompt":"optimize seo title","reason":"swarm"}'
                    )
                ]
            )
        )
        swarm_department = next(
            item for item in supervisor.list_departments() if item.name == "seo_swarm"
        )

        response = await supervisor.run("optimize seo title", campaign_id="cmp-dept")

        assert response.content == "swarm-config::OPTIMIZE SEO TITLE"
        assert response.raw["dynamic_orchestration"]["department_name"] == "seo_swarm"
        assert len(swarm_department.target.event_bus.list_events(topic="need_seo")) == 1
        assert len(swarm_department.target.event_bus.list_events(topic="seo_done")) == 1
