from datetime import datetime, timezone
from uuid import UUID

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

from app.auth.demo_auth import get_admin_user, get_current_user
from app.database import get_db
from app.models.sync import DataSourceConfig, SyncJob
from app.models.user import User

router = APIRouter(prefix="/sync", tags=["Sync"])


# --- Request models ---

class UpdateSourceConfigRequest(BaseModel):
    display_name: str | None = None
    is_enabled: bool | None = None
    sync_interval_minutes: int | None = None
    config: dict | None = None


# --- Routes ---

@router.get("/sources")
async def list_sources(db: AsyncSession = Depends(get_db), _=Depends(get_current_user)):
    """List all data source configurations."""
    result = await db.execute(select(DataSourceConfig).order_by(DataSourceConfig.display_name))
    sources = result.scalars().all()
    return [
        {
            "id": str(s.id),
            "name": s.name,
            "display_name": s.display_name,
            "sync_type": s.sync_type,
            "is_enabled": s.is_enabled,
            "sync_interval_minutes": s.sync_interval_minutes,
            "last_sync_at": s.last_sync_at.isoformat() if s.last_sync_at else None,
            "last_sync_status": s.last_sync_status or "never",
            "last_sync_records": s.last_sync_records,
            "created_at": s.created_at.isoformat(),
            "updated_at": s.updated_at.isoformat(),
        }
        for s in sources
    ]


@router.put("/sources/{source_id}")
async def update_source(
    source_id: str,
    req: UpdateSourceConfigRequest,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user),
):
    """Update a data source configuration (admin only)."""
    source = await db.get(DataSourceConfig, UUID(source_id))
    if not source:
        raise HTTPException(status_code=404, detail="Data source not found")

    if req.display_name is not None:
        source.display_name = req.display_name
    if req.is_enabled is not None:
        source.is_enabled = req.is_enabled
    if req.sync_interval_minutes is not None:
        source.sync_interval_minutes = req.sync_interval_minutes
    if req.config is not None:
        source.config = req.config

    source.updated_at = datetime.now(timezone.utc)
    await db.flush()

    return {
        "id": str(source.id),
        "name": source.name,
        "display_name": source.display_name,
        "sync_type": source.sync_type,
        "is_enabled": source.is_enabled,
        "sync_interval_minutes": source.sync_interval_minutes,
        "last_sync_at": source.last_sync_at.isoformat() if source.last_sync_at else None,
        "last_sync_status": source.last_sync_status or "never",
        "last_sync_records": source.last_sync_records,
        "updated_at": source.updated_at.isoformat(),
    }


@router.post("/sources/{source_id}/trigger")
async def trigger_sync(
    source_id: str,
    db: AsyncSession = Depends(get_db),
    admin: User = Depends(get_admin_user),
):
    """Trigger a manual sync for a data source (admin only)."""
    source = await db.get(DataSourceConfig, UUID(source_id))
    if not source:
        raise HTTPException(status_code=404, detail="Data source not found")

    if not source.is_enabled:
        raise HTTPException(status_code=400, detail="Data source is disabled. Enable it before triggering a sync.")

    # Create a new sync job record
    job = SyncJob(
        data_source_id=source.id,
        status="running",
        triggered_by=f"manual:{admin.email}",
    )
    db.add(job)
    await db.flush()

    # TODO: In production, dispatch to background task / APScheduler
    # For now, just record the job. The actual sync service will pick it up
    # or the demo mode will simulate completion.

    return {
        "message": f"Sync triggered for {source.display_name or source.name}",
        "job": {
            "id": str(job.id),
            "data_source_id": str(source.id),
            "data_source_name": source.name,
            "status": job.status,
            "triggered_by": job.triggered_by,
            "started_at": job.started_at.isoformat(),
        },
    }


@router.get("/jobs")
async def list_jobs(
    limit: int = Query(20, le=100),
    data_source_id: str | None = None,
    db: AsyncSession = Depends(get_db),
    _=Depends(get_current_user),
):
    """List recent sync jobs, optionally filtered by data source."""
    query = select(SyncJob).order_by(SyncJob.started_at.desc()).limit(limit)
    if data_source_id:
        query = query.where(SyncJob.data_source_id == UUID(data_source_id))

    result = await db.execute(query)
    jobs = result.scalars().all()

    items = []
    for job in jobs:
        source = await db.get(DataSourceConfig, job.data_source_id)
        items.append({
            "id": str(job.id),
            "data_source": {
                "id": str(source.id),
                "name": source.name,
                "display_name": source.display_name,
            } if source else None,
            "status": job.status,
            "started_at": job.started_at.isoformat(),
            "completed_at": job.completed_at.isoformat() if job.completed_at else None,
            "records_processed": job.records_processed,
            "records_created": job.records_created,
            "records_updated": job.records_updated,
            "records_failed": job.records_failed,
            "triggered_by": job.triggered_by,
            "created_at": job.created_at.isoformat(),
        })

    return items


@router.get("/jobs/{job_id}")
async def get_job(
    job_id: str,
    db: AsyncSession = Depends(get_db),
    _=Depends(get_current_user),
):
    """Get details of a single sync job."""
    job = await db.get(SyncJob, UUID(job_id))
    if not job:
        raise HTTPException(status_code=404, detail="Sync job not found")

    source = await db.get(DataSourceConfig, job.data_source_id)

    return {
        "id": str(job.id),
        "data_source": {
            "id": str(source.id),
            "name": source.name,
            "display_name": source.display_name,
        } if source else None,
        "status": job.status,
        "started_at": job.started_at.isoformat(),
        "completed_at": job.completed_at.isoformat() if job.completed_at else None,
        "records_processed": job.records_processed,
        "records_created": job.records_created,
        "records_updated": job.records_updated,
        "records_failed": job.records_failed,
        "error_log": job.error_log,
        "triggered_by": job.triggered_by,
        "created_at": job.created_at.isoformat(),
    }
