import asyncio
from uuid import UUID, uuid4

import pytest

from src.connectors.mock.mock_connector import MockConnector
from src.connectors.base import ConnectorResult
from src.core.exceptions import ConnectorException


@pytest.fixture
def mock_connector() -> MockConnector:
    return MockConnector()


@pytest.fixture
def pharmacy_id() -> UUID:
    return uuid4()


@pytest.fixture
def medication_id() -> UUID:
    return uuid4()


class TestMockConnectorConfiguration:
    def test_default_configuration(self, mock_connector: MockConnector):
        assert mock_connector._simulate_latency is True
        assert mock_connector._error_rate == 0.0

    def test_configure_latency(self, mock_connector: MockConnector):
        mock_connector.configure({"simulate_latency": False, "latency_range_ms": [5, 10]})
        assert mock_connector._simulate_latency is False
        assert mock_connector._latency_range_ms == (5, 10)

    def test_configure_error_rate(self, mock_connector: MockConnector):
        mock_connector.configure({"error_rate": 0.5})
        assert mock_connector._error_rate == 0.5


class TestMockConnectorGetStock:
    @pytest.mark.asyncio
    async def test_get_stock_all(self, mock_connector: MockConnector, pharmacy_id: UUID):
        mock_connector.configure({"simulate_latency": False})
        result = await mock_connector.get_stock(pharmacy_id)
        assert result.success is True
        assert "items" in result.data
        assert result.data["total"] == 10

    @pytest.mark.asyncio
    async def test_get_stock_by_nregistro(self, mock_connector: MockConnector, pharmacy_id: UUID):
        mock_connector.configure({"simulate_latency": False})
        result = await mock_connector.get_stock(pharmacy_id, nregistro="00001")
        assert result.success is True
        assert result.data["nregistro"] == "00001"
        assert result.data["name"] == "Ibuprofeno 600mg"

    @pytest.mark.asyncio
    async def test_get_stock_not_found(self, mock_connector: MockConnector, pharmacy_id: UUID):
        mock_connector.configure({"simulate_latency": False})
        result = await mock_connector.get_stock(pharmacy_id, nregistro="99999")
        assert result.success is False
        assert "not found" in result.error


class TestMockConnectorReserve:
    @pytest.mark.asyncio
    async def test_reserve(self, mock_connector: MockConnector, pharmacy_id: UUID, medication_id: UUID):
        mock_connector.configure({"simulate_latency": False})
        result = await mock_connector.reserve(pharmacy_id, medication_id, 5)
        assert result.success is True
        assert result.data["status"] == "confirmed"
        assert result.data["quantity"] == 5

    @pytest.mark.asyncio
    async def test_reserve_idempotency(self, mock_connector: MockConnector, pharmacy_id: UUID, medication_id: UUID):
        mock_connector.configure({"simulate_latency": False})
        key = "idem-key-123"
        result1 = await mock_connector.reserve(pharmacy_id, medication_id, 3, idempotency_key=key)
        result2 = await mock_connector.reserve(pharmacy_id, medication_id, 3, idempotency_key=key)
        assert result1.success is True
        assert result2.success is True


class TestMockConnectorCancelReservation:
    @pytest.mark.asyncio
    async def test_cancel_reservation(self, mock_connector: MockConnector, pharmacy_id: UUID):
        mock_connector.configure({"simulate_latency": False})
        reserve_result = await mock_connector.reserve(pharmacy_id, uuid4(), 2)
        reservation_id = reserve_result.data["reservation_id"]
        result = await mock_connector.cancel_reservation(pharmacy_id, reservation_id)
        assert result.success is True
        assert result.data["status"] == "cancelled"

    @pytest.mark.asyncio
    async def test_cancel_nonexistent_reservation(self, mock_connector: MockConnector, pharmacy_id: UUID):
        mock_connector.configure({"simulate_latency": False})
        result = await mock_connector.cancel_reservation(pharmacy_id, "nonexistent-id")
        assert result.success is False


class TestMockConnectorPharmacyInfo:
    @pytest.mark.asyncio
    async def test_pharmacy_information(self, mock_connector: MockConnector, pharmacy_id: UUID):
        mock_connector.configure({"simulate_latency": False})
        result = await mock_connector.pharmacy_information(pharmacy_id)
        assert result.success is True
        assert "FARM-001" in result.data["code"]


class TestMockConnectorSynchronize:
    @pytest.mark.asyncio
    async def test_synchronize(self, mock_connector: MockConnector, pharmacy_id: UUID):
        mock_connector.configure({"simulate_latency": False, "error_rate": 0.0})
        result = await mock_connector.synchronize(pharmacy_id, strategy="full")
        assert result.success is True
        assert result.data["items_total"] == 10
        assert result.data["items_failed"] == 0


class TestMockConnectorHeartbeat:
    @pytest.mark.asyncio
    async def test_heartbeat(self, mock_connector: MockConnector, pharmacy_id: UUID):
        result = await mock_connector.heartbeat(pharmacy_id)
        assert result.success is True
        assert result.data["status"] == "healthy"
        assert result.data["connector_type"] == "mock"


class TestMockConnectorErrorSimulation:
    def test_high_error_rate_triggers_exception(self, mock_connector: MockConnector):
        mock_connector.configure({"error_rate": 1.0})
        with pytest.raises(ConnectorException):
            mock_connector._maybe_error()

    def test_zero_error_rate_no_exception(self, mock_connector: MockConnector):
        mock_connector.configure({"error_rate": 0.0})
        mock_connector._maybe_error()
