"""Integration tests for intelligent eval task queue API."""

from datetime import timedelta

import pytest
from fastapi.testclient import TestClient
from sqlmodel import Session, SQLModel, create_engine, select

from agenteval.intelligent_eval.models import IntelligentEvalStatus
from agenteval.storage.db import IntelligentEvalDB, IntelligentEvalTaskQueueDB, utc_now
from agenteval.web.app import app
from agenteval.web.deps import get_db


@pytest.fixture()
def client(tmp_path):
    """Create a TestClient with a fresh database."""
    from agenteval.storage.db import (  # noqa: F401
        IntelligentEvalDB,
        IntelligentEvalSessionDB,
        IntelligentEvalTaskQueueDB,
    )

    engine = create_engine(
        f"sqlite:///{tmp_path / 'test.db'}",
        connect_args={"check_same_thread": False},
    )
    SQLModel.metadata.create_all(engine)
    session = Session(engine)

    def override_get_db():
        try:
            yield session
        finally:
            pass

    app.dependency_overrides[get_db] = override_get_db
    client = TestClient(app)
    yield client
    app.dependency_overrides.clear()
    session.close()
    engine.dispose()


@pytest.fixture()
def db_session(client):
    """Get the database session from the client fixture."""
    # The session is stored in the dependency override
    return next(app.dependency_overrides[get_db]())


def test_get_next_task_empty(client: TestClient):
    """Test getting next task when queue is empty."""
    response = client.get("/api/intelligent-evals/tasks/next")
    assert response.status_code == 200
    assert response.json() == {"task": None}


def test_get_next_task_with_pending_task(client: TestClient, db_session: Session):
    """Test getting next task when there is a pending task."""
    # Create eval
    eval_db = IntelligentEvalDB(
        name="test",
        target_id="target1",
        status=IntelligentEvalStatus.EXECUTING.value,
        started_at=utc_now() - timedelta(hours=9),
    )
    eval_db.set_plan({
        "time_distribution": [{"time_slot": "8-10h", "sessions": 2}],
        "estimated_sessions": 2,
    })
    db_session.add(eval_db)
    db_session.commit()

    # Create pending task
    task = IntelligentEvalTaskQueueDB(
        eval_id=eval_db.id,
        status="pending",
        priority=10,
        reason="slot_due",
    )
    db_session.add(task)
    db_session.commit()

    # Get next task
    response = client.get("/api/intelligent-evals/tasks/next")
    assert response.status_code == 200

    data = response.json()
    assert data["task"] is not None
    assert data["task"]["id"] == task.id
    assert data["task"]["eval_id"] == eval_db.id
    assert data["task"]["priority"] == 10
    assert data["task"]["reason"] == "slot_due"
    assert data["task"]["eval"]["id"] == eval_db.id
    assert data["task"]["eval"]["name"] == "test"
    assert data["task"]["eval"]["status"] == IntelligentEvalStatus.EXECUTING.value


def test_assign_task(client: TestClient, db_session: Session):
    """Test assigning a task to a cron."""
    # Create pending task
    task = IntelligentEvalTaskQueueDB(
        eval_id="eval1",
        status="pending",
        priority=1,
        reason="slot_due",
    )
    db_session.add(task)
    db_session.commit()

    # Assign task
    response = client.post(f"/api/intelligent-evals/tasks/{task.id}/assign?cron_id=cron1")
    assert response.status_code == 200
    assert response.json() == {"success": True}

    # Verify assignment
    db_session.refresh(task)
    assert task.status == "assigned"
    assert task.assigned_cron_id == "cron1"


def test_assign_task_not_found(client: TestClient):
    """Test assigning a non-existent task."""
    response = client.post("/api/intelligent-evals/tasks/nonexistent/assign?cron_id=cron1")
    assert response.status_code == 404


def test_complete_task(client: TestClient, db_session: Session):
    """Test completing a task."""
    # Create assigned task
    task = IntelligentEvalTaskQueueDB(
        eval_id="eval1",
        status="assigned",
        priority=1,
        reason="slot_due",
        assigned_cron_id="cron1",
    )
    db_session.add(task)
    db_session.commit()

    # Complete task
    response = client.post(f"/api/intelligent-evals/tasks/{task.id}/complete?success=true")
    assert response.status_code == 200
    assert response.json() == {"success": True}

    # Verify completion
    db_session.refresh(task)
    assert task.status == "completed"
    assert task.completed_at is not None


def test_complete_task_with_error(client: TestClient, db_session: Session):
    """Test completing a task with error."""
    # Create assigned task
    task = IntelligentEvalTaskQueueDB(
        eval_id="eval1",
        status="assigned",
        priority=1,
        reason="slot_due",
        assigned_cron_id="cron1",
    )
    db_session.add(task)
    db_session.commit()

    # Complete task with error
    response = client.post(f"/api/intelligent-evals/tasks/{task.id}/complete?success=false&error=test_error")
    assert response.status_code == 200
    assert response.json() == {"success": True}

    # Verify completion
    db_session.refresh(task)
    assert task.status == "failed"
    assert task.error == "test_error"


def test_end_to_end_task_lifecycle(client: TestClient, db_session: Session):
    """Test end-to-end task lifecycle: create eval -> scan -> enqueue -> assign -> complete."""
    # Create eval that needs attention
    eval_db = IntelligentEvalDB(
        name="test",
        target_id="target1",
        status=IntelligentEvalStatus.EXECUTING.value,
        started_at=utc_now() - timedelta(hours=9),
    )
    eval_db.set_plan({
        "time_distribution": [{"time_slot": "8-10h", "sessions": 2}],
        "estimated_sessions": 2,
    })
    db_session.add(eval_db)
    db_session.commit()

    # Scan and enqueue tasks
    from agenteval.intelligent_eval import task_queue

    enqueued = task_queue.scan_and_enqueue_tasks(db_session)
    assert enqueued == 1

    # Get next task
    response = client.get("/api/intelligent-evals/tasks/next")
    assert response.status_code == 200
    task_data = response.json()["task"]
    assert task_data is not None
    assert task_data["eval_id"] == eval_db.id

    # Assign task
    response = client.post(f"/api/intelligent-evals/tasks/{task_data['id']}/assign?cron_id=cron1")
    assert response.status_code == 200

    # Complete task
    response = client.post(f"/api/intelligent-evals/tasks/{task_data['id']}/complete?success=true")
    assert response.status_code == 200

    # Verify task completed
    task = db_session.get(IntelligentEvalTaskQueueDB, task_data["id"])
    assert task.status == "completed"
