"""Unit tests for observability features: structured logging, metrics, health check"""

import json
import logging

import pytest
from fastapi.testclient import TestClient
from unittest.mock import AsyncMock, MagicMock

from agentkit.core.logging import StructuredFormatter, setup_structured_logging, get_logger
from agentkit.core.protocol import TaskStatus
from agentkit.llm.gateway import LLMGateway
from agentkit.llm.protocol import LLMResponse, TokenUsage
from agentkit.server.app import create_app
from agentkit.skills.base import Skill, SkillConfig
from agentkit.skills.registry import SkillRegistry
from agentkit.tools.registry import ToolRegistry


# ── Structured Logging Tests ────────────────────────────────────────


class TestStructuredFormatter:
    """StructuredFormatter outputs valid JSON with required fields"""

    def test_outputs_valid_json(self):
        formatter = StructuredFormatter()
        record = logging.LogRecord(
            name="agentkit.test",
            level=logging.INFO,
            pathname="test.py",
            lineno=1,
            msg="hello world",
            args=(),
            exc_info=None,
        )
        output = formatter.format(record)
        data = json.loads(output)
        assert "timestamp" in data
        assert data["level"] == "INFO"
        assert data["logger"] == "agentkit.test"
        assert data["message"] == "hello world"

    def test_includes_extra_fields(self):
        formatter = StructuredFormatter()
        record = logging.LogRecord(
            name="agentkit.test",
            level=logging.INFO,
            pathname="test.py",
            lineno=1,
            msg="with extras",
            args=(),
            exc_info=None,
        )
        record.trace_id = "abc-123"
        record.agent_name = "my_agent"
        record.skill_name = "my_skill"
        record.task_id = "task-456"
        output = formatter.format(record)
        data = json.loads(output)
        assert data["trace_id"] == "abc-123"
        assert data["agent_name"] == "my_agent"
        assert data["skill_name"] == "my_skill"
        assert data["task_id"] == "task-456"

    def test_omits_empty_extra_fields(self):
        formatter = StructuredFormatter()
        record = logging.LogRecord(
            name="agentkit.test",
            level=logging.INFO,
            pathname="test.py",
            lineno=1,
            msg="no extras",
            args=(),
            exc_info=None,
        )
        output = formatter.format(record)
        data = json.loads(output)
        assert "trace_id" not in data
        assert "agent_name" not in data

    def test_includes_exception_info(self):
        formatter = StructuredFormatter()
        try:
            raise ValueError("test error")
        except ValueError:
            import sys
            exc_info = sys.exc_info()

        record = logging.LogRecord(
            name="agentkit.test",
            level=logging.ERROR,
            pathname="test.py",
            lineno=1,
            msg="error occurred",
            args=(),
            exc_info=exc_info,
        )
        output = formatter.format(record)
        data = json.loads(output)
        assert "exception" in data
        assert "ValueError" in data["exception"]
        assert "test error" in data["exception"]

    def test_unicode_message(self):
        formatter = StructuredFormatter()
        record = logging.LogRecord(
            name="agentkit.test",
            level=logging.INFO,
            pathname="test.py",
            lineno=1,
            msg="中文日志消息",
            args=(),
            exc_info=None,
        )
        output = formatter.format(record)
        data = json.loads(output)
        assert data["message"] == "中文日志消息"


class TestSetupStructuredLogging:
    """setup_structured_logging() configures agentkit logger"""

    def test_configures_agentkit_logger(self):
        setup_structured_logging(level=logging.DEBUG)
        logger = logging.getLogger("agentkit")
        assert logger.level == logging.DEBUG
        assert len(logger.handlers) == 1
        handler = logger.handlers[0]
        assert isinstance(handler.formatter, StructuredFormatter)

    def test_clears_existing_handlers(self):
        logger = logging.getLogger("agentkit")
        logger.addHandler(logging.StreamHandler())
        initial_count = len(logger.handlers)

        setup_structured_logging()
        assert len(logger.handlers) == 1
        assert len(logger.handlers) < initial_count + 1


class TestGetLogger:
    """get_logger() creates logger with extra fields"""

    def test_returns_logger_adapter(self):
        adapter = get_logger("my_module")
        assert isinstance(adapter, logging.LoggerAdapter)
        assert adapter.logger.name == "agentkit.my_module"

    def test_extra_fields_in_adapter(self):
        adapter = get_logger("test", trace_id="t-1", agent_name="a-1")
        assert adapter.extra["trace_id"] == "t-1"
        assert adapter.extra["agent_name"] == "a-1"


# ── Metrics Endpoint Tests ─────────────────────────────────────────


@pytest.fixture
def mock_llm_gateway():
    gateway = LLMGateway()
    mock_provider = AsyncMock()
    mock_provider.chat.return_value = LLMResponse(
        content='{"result": "mocked"}',
        model="test-model",
        usage=TokenUsage(prompt_tokens=10, completion_tokens=20),
    )
    gateway.register_provider("test", mock_provider)
    return gateway


@pytest.fixture
def skill_registry():
    return SkillRegistry()


@pytest.fixture
def tool_registry():
    return ToolRegistry()


@pytest.fixture
def app(mock_llm_gateway, skill_registry, tool_registry):
    return create_app(
        llm_gateway=mock_llm_gateway,
        skill_registry=skill_registry,
        tool_registry=tool_registry,
    )


@pytest.fixture
def client(app):
    return TestClient(app)


class TestMetricsEndpoint:
    """GET /api/v1/metrics"""

    def test_metrics_returns_200(self, client):
        response = client.get("/api/v1/metrics")
        assert response.status_code == 200

    def test_metrics_has_required_sections(self, client):
        response = client.get("/api/v1/metrics")
        data = response.json()
        assert "tasks" in data
        assert "agents" in data
        assert "skills" in data
        assert "version" in data

    def test_metrics_zero_values_when_empty(self, client):
        response = client.get("/api/v1/metrics")
        data = response.json()
        assert data["tasks"]["total_tasks"] == 0
        assert data["tasks"]["completed_tasks"] == 0
        assert data["tasks"]["failed_tasks"] == 0
        assert data["tasks"]["pending_tasks"] == 0
        assert data["agents"]["total_agents"] == 0
        assert data["skills"]["total_skills"] == 0

    def test_metrics_with_registered_skill(self, client, skill_registry):
        skill_config = SkillConfig(
            name="metrics_skill",
            agent_type="test_type",
            task_mode="llm_generate",
            prompt={"identity": "Metrics Skill"},
            intent={"keywords": ["metrics"], "description": "A metrics skill"},
        )
        skill = Skill(config=skill_config)
        skill_registry.register(skill)

        response = client.get("/api/v1/metrics")
        data = response.json()
        assert data["skills"]["total_skills"] == 1

    def test_metrics_version(self, client):
        response = client.get("/api/v1/metrics")
        data = response.json()
        assert data["version"] == "2.0.0"


# ── Enhanced Health Check Tests ─────────────────────────────────────


class TestEnhancedHealthCheck:
    """GET /api/v1/health — enhanced with dependency checks"""

    def test_health_returns_200(self, client):
        response = client.get("/api/v1/health")
        assert response.status_code == 200

    def test_health_includes_checks(self, client):
        response = client.get("/api/v1/health")
        data = response.json()
        assert "checks" in data
        assert "redis" in data["checks"]
        assert "agent_pool" in data["checks"]
        assert "llm_gateway" in data["checks"]
        assert "skill_registry" in data["checks"]

    def test_health_healthy_with_provider(self, client):
        """With a registered LLM provider, status should be healthy"""
        response = client.get("/api/v1/health")
        data = response.json()
        assert data["status"] == "healthy"
        assert data["version"] == "2.0.0"

    def test_health_agent_pool_info(self, client):
        response = client.get("/api/v1/health")
        data = response.json()
        pool_check = data["checks"]["agent_pool"]
        assert pool_check["status"] == "available"
        assert pool_check["size"] == 0

    def test_health_skill_registry_info(self, client):
        response = client.get("/api/v1/health")
        data = response.json()
        registry_check = data["checks"]["skill_registry"]
        assert registry_check["status"] == "available"
        assert registry_check["count"] == 0

    def test_health_degraded_without_providers(self, skill_registry, tool_registry):
        """Without LLM providers, status should be degraded"""
        gateway = LLMGateway()  # No providers registered
        app = create_app(
            llm_gateway=gateway,
            skill_registry=skill_registry,
            tool_registry=tool_registry,
        )
        client = TestClient(app)
        response = client.get("/api/v1/health")
        data = response.json()
        assert data["status"] == "degraded"
        assert data["checks"]["llm_gateway"] == "no_providers"

    def test_health_redis_not_configured_for_memory_store(self, client):
        """In-memory task store should report redis as not_configured"""
        response = client.get("/api/v1/health")
        data = response.json()
        assert data["checks"]["redis"] == "not_configured"

    def test_health_llm_gateway_available_with_provider(self, client):
        response = client.get("/api/v1/health")
        data = response.json()
        assert data["checks"]["llm_gateway"] == "available"
