import asyncio
import random
from datetime import datetime, timezone
from typing import Any
from uuid import UUID

from src.connectors.base import BaseInventoryConnector, ConnectorResult
from src.core.logging import get_logger
from src.core.metrics import CONNECTOR_OPERATIONS

logger = get_logger("mock_connector")


class MockConnector(BaseInventoryConnector):
    connector_type: str = "mock"

    def __init__(self) -> None:
        self._simulate_latency: bool = True
        self._latency_range_ms: tuple[int, int] = (20, 150)
        self._error_rate: float = 0.0
        self._stock_data: dict[str, dict[str, Any]] = {}
        self._reservations: dict[str, dict[str, Any]] = {}
        self._pharmacies: dict[str, dict[str, Any]] = {}
        self._init_mock_data()

    def configure(self, config: dict[str, Any]) -> None:
        self._simulate_latency = config.get("simulate_latency", True)
        self._latency_range_ms = tuple(config.get("latency_range_ms", [20, 150]))
        self._error_rate = config.get("error_rate", 0.0)

    def _init_mock_data(self) -> None:
        medicamentos = [
            ("00001", "Ibuprofeno 600mg", 100),
            ("00002", "Paracetamol 1g", 50),
            ("00003", "Amoxicilina 500mg", 200),
            ("00004", "Omeprazol 20mg", 75),
            ("00005", "Loratadina 10mg", 120),
            ("00006", "Diazepam 5mg", 30),
            ("00007", "Metformina 850mg", 250),
            ("00008", "Atorvastatina 20mg", 80),
            ("00009", "Salbutamol Inhalador", 15),
            ("00010", "Insulina Glargina", 40),
        ]
        for nregistro, name, stock in medicamentos:
            reserved = random.randint(0, max(stock // 10, 1))
            self._stock_data[nregistro] = {
                "nregistro": nregistro,
                "name": name,
                "stock": stock,
                "reserved_stock": reserved,
                "available_stock": stock - reserved,
                "price": round(random.uniform(2.5, 45.0), 2),
                "last_updated": datetime.now(timezone.utc).isoformat(),
            }

        self._pharmacies["default"] = {
            "code": "FARM-001",
            "name": "Farmacia Mock Central",
            "address": "Calle Mayor 15, Madrid",
            "phone": "+34912345678",
            "email": "central@mock-farmacia.es",
            "opening_hours": {
                "mon_fri": ["09:00", "21:30"],
                "sat": ["10:00", "14:00"],
                "sun": "closed",
            },
            "latitude": 40.4168,
            "longitude": -3.7038,
        }
        self._pharmacies["second"] = {
            "code": "FARM-002",
            "name": "Farmacia Mock Norte",
            "address": "Av. de la Ilustración 1, Madrid",
            "phone": "+34915432100",
            "email": "norte@mock-farmacia.es",
            "opening_hours": {
                "mon_fri": ["08:30", "22:00"],
                "sat": ["09:00", "22:00"],
                "sun": ["10:00", "14:00"],
            },
            "latitude": 40.4722,
            "longitude": -3.6934,
        }

    async def _maybe_delay(self) -> None:
        if self._simulate_latency:
            low, high = self._latency_range_ms
            delay = random.randint(low, high) / 1000.0
            await asyncio.sleep(delay)

    def _maybe_error(self) -> None:
        if self._error_rate > 0 and random.random() < self._error_rate:
            error_types = [
                ("TIMEOUT", "Connection timed out"),
                ("CONNECTION_REFUSED", "ERP server refused connection"),
                ("AUTH_FAILED", "Authentication failed"),
                ("RATE_LIMITED", "ERP rate limit exceeded"),
                ("INTERNAL_ERROR", "ERP internal server error"),
            ]
            code, msg = random.choice(error_types)
            from src.core.exceptions import ConnectorException
            raise ConnectorException(self.connector_type, msg, details={"error_code": code})

    def _metric(self, operation: str, status: str) -> None:
        CONNECTOR_OPERATIONS.labels(connector_type=self.connector_type, operation=operation, status=status).inc()

    async def get_stock(self, pharmacy_id: UUID, medication_id: UUID | None = None, nregistro: str | None = None) -> ConnectorResult:
        await self._maybe_delay()
        self._maybe_error()
        logger.info("mock_get_stock", extra={"pharmacy_id": str(pharmacy_id), "nregistro": nregistro})

        if nregistro and nregistro in self._stock_data:
            data = dict(self._stock_data[nregistro])
            data["pharmacy_id"] = str(pharmacy_id)
            self._metric("get_stock", "success")
            return ConnectorResult(success=True, data=data)

        if nregistro is None and medication_id is None:
            results = [dict(v) for v in self._stock_data.values()]
            for r in results:
                r["pharmacy_id"] = str(pharmacy_id)
            self._metric("get_stock", "success")
            return ConnectorResult(success=True, data={"items": results, "total": len(results)})

        self._metric("get_stock", "not_found")
        return ConnectorResult(success=False, error=f"Medication '{nregistro}' not found in mock data")

    async def reserve(self, pharmacy_id: UUID, medication_id: UUID, quantity: int, idempotency_key: str | None = None) -> ConnectorResult:
        await self._maybe_delay()
        self._maybe_error()
        logger.info("mock_reserve", extra={"pharmacy_id": str(pharmacy_id), "quantity": quantity})

        if idempotency_key and idempotency_key in self._reservations:
            self._metric("reserve", "idempotent")
            return ConnectorResult(success=True, data=self._reservations[idempotency_key])

        reservation_id = f"mock-res-{idempotency_key or random.randint(100000, 999999)}"
        reservation_data = {
            "reservation_id": reservation_id,
            "status": "confirmed",
            "quantity": quantity,
            "pharmacy_id": str(pharmacy_id),
            "medication_id": str(medication_id),
            "confirmed_at": datetime.now(timezone.utc).isoformat(),
            "expires_at": datetime.now(timezone.utc).isoformat(),
        }
        self._reservations[reservation_id] = reservation_data
        if idempotency_key:
            self._reservations[idempotency_key] = reservation_data

        self._metric("reserve", "success")
        return ConnectorResult(success=True, data=reservation_data)

    async def cancel_reservation(self, pharmacy_id: UUID, reservation_id: str) -> ConnectorResult:
        await self._maybe_delay()
        self._maybe_error()
        logger.info("mock_cancel_reservation", extra={"reservation_id": reservation_id})

        if reservation_id in self._reservations:
            self._reservations[reservation_id]["status"] = "cancelled"
            self._metric("cancel_reservation", "success")
            return ConnectorResult(success=True, data={"reservation_id": reservation_id, "status": "cancelled"})

        self._metric("cancel_reservation", "not_found")
        return ConnectorResult(success=False, error=f"Reservation '{reservation_id}' not found")

    async def pharmacy_information(self, pharmacy_id: UUID) -> ConnectorResult:
        await self._maybe_delay()
        self._maybe_error()
        logger.info("mock_pharmacy_information", extra={"pharmacy_id": str(pharmacy_id)})

        pharmacy = list(self._pharmacies.values())[0]
        result = dict(pharmacy)
        result["pharmacy_id"] = str(pharmacy_id)
        self._metric("pharmacy_information", "success")
        return ConnectorResult(success=True, data=result)

    async def synchronize(self, pharmacy_id: UUID, strategy: str = "incremental", since: str | None = None) -> ConnectorResult:
        await self._maybe_delay()
        self._maybe_error()
        logger.info("mock_synchronize", extra={"pharmacy_id": str(pharmacy_id), "strategy": strategy, "since": since})

        items_total = len(self._stock_data)
        items_processed = items_total
        items_failed = random.randint(0, min(2, items_total)) if self._error_rate > 0 else 0
        if items_failed > 0:
            items_processed = items_total - items_failed

        self._metric("synchronize", "success")
        return ConnectorResult(
            success=True,
            data={
                "items_total": items_total,
                "items_processed": items_processed,
                "items_failed": items_failed,
                "strategy": strategy,
                "synced_at": datetime.now(timezone.utc).isoformat(),
                "pharmacy_id": str(pharmacy_id),
            },
        )

    async def heartbeat(self, pharmacy_id: UUID) -> ConnectorResult:
        self._metric("heartbeat", "success")
        return ConnectorResult(
            success=True,
            data={
                "status": "healthy",
                "timestamp": datetime.now(timezone.utc).isoformat(),
                "connector_type": self.connector_type,
                "pharmacy_id": str(pharmacy_id),
                "circuit_breaker": "closed",
                "stock_items": len(self._stock_data),
                "active_reservations": len([r for r in self._reservations.values() if r.get("status") == "confirmed"]),
            },
        )
