import json
import asyncio
from fastapi import APIRouter, Depends
from fastapi.encoders import jsonable_encoder
from sqlalchemy.orm import Session
from starlette.responses import StreamingResponse
from app.models import (
    Jobs as JobModel,
    ExperimentParameters as ExperimentParametersModel,
    Sample as SampleModel,
)
from app.schemas import JobsResponse, JobsUpdate
from app.dependencies import get_db

router = APIRouter()


async def job_event_generator(get_db):
    while True:
        # Open a new session for this iteration and close it at the end
        with next(get_db()) as db:
            jobs = db.query(JobModel).all()
            job_items = []
            for job in jobs:
                sample = db.query(SampleModel).filter_by(id=job.sample_id).first()
                experiment = (
                    db.query(ExperimentParametersModel)
                    .filter(
                        ExperimentParametersModel.sample_id == sample.id,
                        ExperimentParametersModel.id == job.run_id,
                    )
                    .first()
                )

                job_item = JobsResponse(
                    job_id=job.id,
                    sample_id=sample.id,
                    run_id=job.run_id,
                    sample_name=sample.sample_name,
                    status=job.status,
                    type=experiment.type if experiment else None,
                    created_at=job.created_at,
                    updated_at=job.updated_at,
                    data_collection_parameters=sample.data_collection_parameters,
                    experiment_parameters=experiment.beamline_parameters
                    if experiment
                    else None,
                    filepath=experiment.dataset.get("filepath")
                    if experiment and experiment.dataset
                    else None,
                    slurm_id=job.slurm_id,
                )
                job_items.append(job_item)

            if job_items:
                serialized = jsonable_encoder(job_items)
                yield f"data: {json.dumps(serialized)}\n\n"

        await asyncio.sleep(5)


# A reasonable heartbeat/refresh


@router.get("/jobs/stream")
async def stream_jobs():
    # Pass the dependency itself, not an active session
    from app.dependencies import get_db

    return StreamingResponse(
        job_event_generator(get_db), media_type="text/event-stream"
    )


@router.post(
    "/jobs/update_status", response_model=JobsUpdate, operation_id="update_status"
)
def update_jobs_status(payload: JobsUpdate, db: Session = Depends(get_db)):
    # Fetch the job by job_id
    job = db.query(JobModel).filter(JobModel.id == payload.job_id).first()
    if not job:
        # Optionally, use HTTPException for proper status code
        from fastapi import HTTPException

        raise HTTPException(status_code=404, detail="Job not found")

    # If status is being updated to "cancelled"
    if payload.status == "cancelled":
        job.slurm_id = None

    # Update the status
    job.status = payload.status
    job.slurm_id = payload.slurm_id
    # Optionally update 'updated_at'
    from datetime import datetime

    job.updated_at = datetime.now()

    db.commit()
    db.refresh(job)

    # Return the updated job's info as response
    return JobsUpdate(job_id=job.id, status=job.status, slurm_id=job.slurm_id)


def cleanup_cancelled_jobs(db: Session):
    from datetime import datetime
    from datetime import timedelta

    """Delete jobs in 'cancelled' state for more than 2 hours."""
    cutoff = datetime.now() - timedelta(hours=2)
    print(
        f"Cleaning up cancelled jobs older than {cutoff} "
        f"(current time: {datetime.now()})"
    )
    old_jobs = (
        db.query(JobModel)
        .filter(JobModel.status == "cancelled", JobModel.updated_at < cutoff)
        .all()
    )
    for job in old_jobs:
        db.delete(job)
    db.commit()
