"""Tests for the rewritten hooks server."""

import asyncio
import json
import pytest
from aiohttp import web
from aiohttp.test_utils import AioHTTPTestCase, unittest_run_loop, TestClient, TestServer
from nanobot.hooks.server import HooksServer
from nanobot.bus.queue import MessageBus
from nanobot.bus.events import InboundMessage, OutboundMessage
from nanobot.config.schema import HooksConfig


@pytest.fixture
def bus():
    return MessageBus()


@pytest.fixture
def config():
    return HooksConfig(
        enabled=True,
        tokens={"test-hook": "test-secret-123"},
        timeout_seconds=5,
    )


@pytest.fixture
def server(bus, config):
    return HooksServer(host="127.0.0.1", port=0, config=config, bus=bus)


@pytest.mark.asyncio
async def test_health_check(server):
    client = TestClient(TestServer(server._app))
    async with client:
        resp = await client.get("/health")
        assert resp.status == 200
        data = await resp.json()
        assert data["status"] == "ok"


@pytest.mark.asyncio
async def test_unauthorized_without_token(server):
    client = TestClient(TestServer(server._app))
    async with client:
        resp = await client.post("/hooks", json={"message": "test"})
        assert resp.status == 401


@pytest.mark.asyncio
async def test_unauthorized_wrong_token(server):
    client = TestClient(TestServer(server._app))
    async with client:
        resp = await client.post(
            "/hooks",
            json={"message": "test"},
            headers={"Authorization": "Bearer wrong-token"},
        )
        assert resp.status == 401


@pytest.mark.asyncio
async def test_missing_message_field(server, bus):
    client = TestClient(TestServer(server._app))
    async with client:
        resp = await client.post(
            "/hooks",
            json={"not_message": "test"},
            headers={"Authorization": "Bearer test-secret-123"},
        )
        assert resp.status == 400


@pytest.mark.asyncio
async def test_hook_publishes_to_bus(server, bus):
    """Hook should publish InboundMessage to bus and the message should contain hook prefix."""
    client = TestClient(TestServer(server._app))
    async with client:
        # Send hook request in background (it will block waiting for correlation)
        async def send_request():
            return await client.post(
                "/hooks",
                json={"message": "hello from webhook"},
                headers={"Authorization": "Bearer test-secret-123"},
            )

        task = asyncio.create_task(send_request())

        # Consume the inbound message
        msg = await asyncio.wait_for(bus.consume_inbound(), timeout=2.0)

        assert msg.channel == "hook"
        assert msg.chat_id == "test-hook"  # defaults to token name
        assert msg.metadata.get("hook_source") == "test-hook"
        assert msg.metadata.get("correlation_id") is not None

        # Simulate agent response by resolving correlation
        bus.resolve_correlation(OutboundMessage(
            channel="hook",
            chat_id="test-hook",
            content="agent says hi",
            metadata={"correlation_id": msg.metadata["correlation_id"]},
        ))

        resp = await asyncio.wait_for(task, timeout=2.0)
        assert resp.status == 200
        data = await resp.json()
        assert data["ok"] is True
        assert data["response"] == "agent says hi"


@pytest.mark.asyncio
async def test_hook_with_custom_channel(server, bus):
    """Hook targeting telegram should use telegram channel in InboundMessage."""
    client = TestClient(TestServer(server._app))
    async with client:
        async def send_request():
            return await client.post(
                "/hooks",
                json={"message": "notify user", "channel": "telegram", "chat_id": "239824268"},
                headers={"Authorization": "Bearer test-secret-123"},
            )

        task = asyncio.create_task(send_request())

        msg = await asyncio.wait_for(bus.consume_inbound(), timeout=2.0)
        assert msg.channel == "telegram"
        assert msg.chat_id == "239824268"
        assert msg.session_key == "telegram:239824268"

        bus.resolve_correlation(OutboundMessage(
            channel="telegram",
            chat_id="239824268",
            content="done",
            metadata={"correlation_id": msg.metadata["correlation_id"]},
        ))

        resp = await asyncio.wait_for(task, timeout=2.0)
        assert resp.status == 200
        data = await resp.json()
        assert data["response"] == "done"


@pytest.mark.asyncio
async def test_hook_timeout_returns_504(bus):
    """If agent doesn't respond in time, return 504."""
    config = HooksConfig(enabled=True, tokens={"test-hook": "test-secret-123"}, timeout_seconds=1)
    server = HooksServer(host="127.0.0.1", port=0, config=config, bus=bus)
    client = TestClient(TestServer(server._app))
    async with client:
        resp = await client.post(
            "/hooks",
            json={"message": "slow request"},
            headers={"Authorization": "Bearer test-secret-123"},
        )
        assert resp.status == 504


@pytest.mark.asyncio
async def test_hook_timeout_zero_returns_202(server, bus):
    """timeout=0 should return 202 immediately without waiting."""
    client = TestClient(TestServer(server._app))
    async with client:
        resp = await client.post(
            "/hooks",
            json={"message": "fire and forget", "timeout": 0},
            headers={"Authorization": "Bearer test-secret-123"},
        )
        assert resp.status == 202
        data = await resp.json()
        assert data["ok"] is True

        # Message should still be on the bus
        msg = await asyncio.wait_for(bus.consume_inbound(), timeout=1.0)
        assert msg.content == "fire and forget"
