from datetime import datetime
from typing import Any
from uuid import UUID

from fastapi import APIRouter, Depends, Query
from pydantic import BaseModel, Field
from sqlalchemy.ext.asyncio import AsyncSession

from src.core.exceptions import NotFoundException
from src.domain.entities import PharmacyEntity, PharmacyStatus
from src.domain.interfaces import IPharmacyRepository
from src.infrastructure.database.session import get_db_session
from src.infrastructure.database.repositories import PharmacyRepository
from src.api.v1.dependencies.auth import get_current_user, require_role
from src.domain.entities import UserRole
from src.services.auth.security import TokenData

router = APIRouter(prefix="/pharmacies", tags=["Pharmacies"])


class PharmacyCreateRequest(BaseModel):
    code: str = Field(..., min_length=1, max_length=50)
    name: str = Field(..., min_length=1, max_length=255)
    address: str | None = None
    city: str | None = None
    province: str | None = None
    postal_code: str | None = None
    country: str = "ES"
    latitude: float | None = None
    longitude: float | None = None
    phone: str | None = None
    email: str | None = None
    opening_hours: dict | None = None
    erp_type: str | None = None
    erp_version: str | None = None
    sync_strategy: str = "incremental"
    sync_interval_minutes: int = 30


class PharmacyUpdateRequest(BaseModel):
    name: str | None = None
    address: str | None = None
    city: str | None = None
    province: str | None = None
    postal_code: str | None = None
    country: str | None = None
    latitude: float | None = None
    longitude: float | None = None
    phone: str | None = None
    email: str | None = None
    opening_hours: dict | None = None
    status: str | None = None
    erp_type: str | None = None
    erp_version: str | None = None
    sync_strategy: str | None = None
    sync_interval_minutes: int | None = None


class PharmacyResponse(BaseModel):
    id: UUID
    code: str
    name: str
    address: str | None
    city: str | None
    province: str | None
    postal_code: str | None
    country: str
    latitude: float | None
    longitude: float | None
    phone: str | None
    email: str | None
    opening_hours: dict | None
    status: str
    erp_type: str | None
    erp_version: str | None
    connector_id: UUID | None
    sync_strategy: str
    sync_interval_minutes: int
    last_sync_at: datetime | None
    last_sync_status: str | None
    created_at: datetime | None
    updated_at: datetime | None


class PharmacyListResponse(BaseModel):
    items: list[PharmacyResponse]
    total: int
    offset: int
    limit: int


def _entity_to_response(e: PharmacyEntity) -> PharmacyResponse:
    return PharmacyResponse(
        id=e.id, code=e.code, name=e.name, address=e.address, city=e.city,
        province=e.province, postal_code=e.postal_code, country=e.country,
        latitude=e.latitude, longitude=e.longitude, phone=e.phone, email=e.email,
        opening_hours=e.opening_hours, status=e.status, erp_type=e.erp_type,
        erp_version=e.erp_version, connector_id=e.connector_id,
        sync_strategy=e.sync_strategy, sync_interval_minutes=e.sync_interval_minutes,
        last_sync_at=e.last_sync_at, last_sync_status=e.last_sync_status,
        created_at=e.created_at, updated_at=e.updated_at,
    )


def _get_repo(session: AsyncSession) -> IPharmacyRepository:
    return PharmacyRepository(session)


@router.get("", response_model=PharmacyListResponse)
async def list_pharmacies(
    status: str | None = None,
    erp_type: str | None = None,
    offset: int = Query(0, ge=0),
    limit: int = Query(50, ge=1, le=200),
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(get_current_user),
):
    repo = _get_repo(session)
    entities, total = await repo.list_pharmacies(status=status, erp_type=erp_type, offset=offset, limit=limit)
    return PharmacyListResponse(items=[_entity_to_response(e) for e in entities], total=total, offset=offset, limit=limit)


@router.get("/{pharmacy_id}", response_model=PharmacyResponse)
async def get_pharmacy(
    pharmacy_id: UUID,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(get_current_user),
):
    repo = _get_repo(session)
    entity = await repo.get_by_id(pharmacy_id)
    if not entity:
        raise NotFoundException("Pharmacy", str(pharmacy_id))
    return _entity_to_response(entity)


@router.post("", response_model=PharmacyResponse, status_code=201)
async def create_pharmacy(
    body: PharmacyCreateRequest,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.ADMIN)),
):
    repo = _get_repo(session)
    entity = PharmacyEntity(
        code=body.code, name=body.name, address=body.address, city=body.city,
        province=body.province, postal_code=body.postal_code, country=body.country,
        latitude=body.latitude, longitude=body.longitude, phone=body.phone,
        email=body.email, opening_hours=body.opening_hours,
        status=PharmacyStatus.ACTIVE.value, erp_type=body.erp_type,
        erp_version=body.erp_version, sync_strategy=body.sync_strategy,
        sync_interval_minutes=body.sync_interval_minutes,
    )
    created = await repo.create(entity)
    return _entity_to_response(created)


@router.put("/{pharmacy_id}", response_model=PharmacyResponse)
async def update_pharmacy(
    pharmacy_id: UUID,
    body: PharmacyUpdateRequest,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.ADMIN)),
):
    repo = _get_repo(session)
    entity = await repo.get_by_id(pharmacy_id)
    if not entity:
        raise NotFoundException("Pharmacy", str(pharmacy_id))

    update_data = body.model_dump(exclude_unset=True)
    for key, value in update_data.items():
        setattr(entity, key, value)

    updated = await repo.update(entity)
    return _entity_to_response(updated)


@router.delete("/{pharmacy_id}", status_code=204)
async def delete_pharmacy(
    pharmacy_id: UUID,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.ADMIN)),
):
    repo = _get_repo(session)
    deleted = await repo.delete(pharmacy_id)
    if not deleted:
        raise NotFoundException("Pharmacy", str(pharmacy_id))
