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.api.v1.dependencies.auth import get_current_user, require_role
from src.core.exceptions import NotFoundException
from src.domain.entities import SyncJobStatus, SyncStrategy, UserRole
from src.infrastructure.database.repositories import SyncJobRepository, SyncLogRepository
from src.infrastructure.database.session import get_db_session
from src.services.auth.security import TokenData
from src.services.synchronization.sync_service import SyncService

router = APIRouter(prefix="/pharmacies/{pharmacy_id}/sync-jobs", tags=["Sync Jobs"])


class SyncJobCreateRequest(BaseModel):
    connector_id: UUID
    strategy: SyncStrategy = SyncStrategy.INCREMENTAL


class SyncJobResponse(BaseModel):
    id: UUID
    pharmacy_id: UUID
    connector_id: UUID
    strategy: str
    status: str
    started_at: datetime | None
    completed_at: datetime | None
    items_total: int | None
    items_processed: int | None
    items_failed: int | None
    error_message: str | None
    metadata: dict[str, Any] | None
    created_at: datetime | None
    updated_at: datetime | None


class SyncJobListResponse(BaseModel):
    items: list[SyncJobResponse]
    total: int
    offset: int
    limit: int


class SyncLogResponse(BaseModel):
    id: UUID
    sync_job_id: UUID
    level: str
    message: str
    details: dict[str, Any] | None
    created_at: datetime | None


def _entity_to_response(e: Any) -> SyncJobResponse:
    return SyncJobResponse(
        id=e.id, pharmacy_id=e.pharmacy_id, connector_id=e.connector_id,
        strategy=e.strategy, status=e.status, started_at=e.started_at,
        completed_at=e.completed_at, items_total=e.items_total,
        items_processed=e.items_processed, items_failed=e.items_failed,
        error_message=e.error_message, metadata=e.metadata_,
        created_at=e.created_at, updated_at=e.updated_at,
    )


def _get_service(session: AsyncSession) -> SyncService:
    return SyncService(
        sync_job_repo=SyncJobRepository(session),
        sync_log_repo=SyncLogRepository(session),
    )


@router.get("", response_model=SyncJobListResponse)
async def list_sync_jobs(
    pharmacy_id: UUID,
    status: 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),
):
    service = _get_service(session)
    entities, total = await service.list_sync_jobs(pharmacy_id, status=status, offset=offset, limit=limit)
    return SyncJobListResponse(items=[_entity_to_response(e) for e in entities], total=total, offset=offset, limit=limit)


@router.get("/{job_id}", response_model=SyncJobResponse)
async def get_sync_job(
    pharmacy_id: UUID,
    job_id: UUID,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(get_current_user),
):
    service = _get_service(session)
    entity = await service.get_sync_job(job_id)
    if not entity or entity.pharmacy_id != pharmacy_id:
        raise NotFoundException("SyncJob", str(job_id))
    return _entity_to_response(entity)


@router.post("", response_model=SyncJobResponse, status_code=201)
async def create_sync_job(
    pharmacy_id: UUID,
    body: SyncJobCreateRequest,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.OPERATOR)),
):
    service = _get_service(session)
    created = await service.create_sync_job(
        pharmacy_id=pharmacy_id,
        connector_id=body.connector_id,
        strategy=body.strategy,
    )
    return _entity_to_response(created)


@router.post("/{job_id}/trigger", response_model=SyncJobResponse)
async def trigger_sync(
    pharmacy_id: UUID,
    job_id: UUID,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.OPERATOR)),
):
    service = _get_service(session)
    updated = await service.trigger_sync(job_id)
    return _entity_to_response(updated)


@router.post("/{job_id}/cancel", response_model=SyncJobResponse)
async def cancel_sync(
    pharmacy_id: UUID,
    job_id: UUID,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.OPERATOR)),
):
    service = _get_service(session)
    updated = await service.cancel_sync(job_id)
    return _entity_to_response(updated)


class SyncCompleteRequest(BaseModel):
    items_total: int | None = None
    items_processed: int | None = None
    items_failed: int | None = None
    duration_ms: int = 0


class SyncFailRequest(BaseModel):
    error_message: str


@router.post("/{job_id}/complete", response_model=SyncJobResponse)
async def complete_sync(
    pharmacy_id: UUID,
    job_id: UUID,
    body: SyncCompleteRequest,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.OPERATOR)),
):
    service = _get_service(session)
    updated = await service.complete_sync(job_id, items_total=body.items_total, items_processed=body.items_processed, items_failed=body.items_failed)
    return _entity_to_response(updated)


@router.post("/{job_id}/fail", response_model=SyncJobResponse)
async def fail_sync(
    pharmacy_id: UUID,
    job_id: UUID,
    body: SyncFailRequest,
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(require_role(UserRole.OPERATOR)),
):
    service = _get_service(session)
    updated = await service.fail_sync(job_id, error_message=body.error_message)
    return _entity_to_response(updated)


@router.get("/{job_id}/logs", response_model=list[SyncLogResponse])
async def get_sync_logs(
    pharmacy_id: UUID,
    job_id: UUID,
    level: str | None = None,
    offset: int = Query(0, ge=0),
    limit: int = Query(100, ge=1, le=500),
    session: AsyncSession = Depends(get_db_session),
    current_user: TokenData = Depends(get_current_user),
):
    service = _get_service(session)
    logs = await service.get_sync_logs(job_id, level=level, offset=offset, limit=limit)
    return [
        SyncLogResponse(
            id=l.id, sync_job_id=l.sync_job_id, level=l.level,
            message=l.message, details=l.details, created_at=l.created_at,
        )
        for l in logs
    ]
