"""Integration tests for worker skill APIs (heartbeat, decision logs)."""

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,
    IntelligentEvalDecisionLogDB,
    OpenClawCronPoolDB,
    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,
        IntelligentEvalDecisionLogDB,
        OpenClawCronPoolDB,
    )

    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."""
    return next(app.dependency_overrides[get_db]())


def test_heartbeat_idle(client: TestClient, db_session: Session):
    """Test heartbeat from idle cron."""
    # Create cron
    cron = OpenClawCronPoolDB(
        openclaw_cron_id="cron-123",
        status="idle",
        last_active_at=utc_now() - timedelta(minutes=5),
    )
    db_session.add(cron)
    db_session.commit()

    old_active_at = cron.last_active_at

    # Report heartbeat
    response = client.post(
        "/api/openclaw/crons/cron-123/heartbeat",
        json={"status": "idle", "current_eval_id": None},
    )
    assert response.status_code == 200
    assert response.json() == {"success": True}

    # Verify heartbeat updated
    db_session.refresh(cron)
    assert cron.last_active_at > old_active_at
    assert cron.status == "idle"
    assert cron.current_eval_id is None


def test_heartbeat_busy(client: TestClient, db_session: Session):
    """Test heartbeat from busy cron."""
    # Create cron
    cron = OpenClawCronPoolDB(
        openclaw_cron_id="cron-456",
        status="busy",
        current_eval_id="eval-789",
        last_active_at=utc_now() - timedelta(minutes=2),
    )
    db_session.add(cron)
    db_session.commit()

    # Report heartbeat
    response = client.post(
        "/api/openclaw/crons/cron-456/heartbeat",
        json={"status": "busy", "current_eval_id": "eval-789"},
    )
    assert response.status_code == 200

    # Verify heartbeat updated
    db_session.refresh(cron)
    assert cron.status == "busy"
    assert cron.current_eval_id == "eval-789"


def test_heartbeat_not_found(client: TestClient):
    """Test heartbeat from non-existent cron."""
    response = client.post(
        "/api/openclaw/crons/nonexistent/heartbeat",
        json={"status": "idle", "current_eval_id": None},
    )
    assert response.status_code == 404


def test_create_decision_log(client: TestClient, db_session: Session):
    """Test creating a decision log."""
    # Create eval
    eval_db = IntelligentEvalDB(
        name="test",
        target_id="target1",
        status=IntelligentEvalStatus.EXECUTING.value,
    )
    db_session.add(eval_db)
    db_session.commit()

    # Create decision log
    response = client.post(
        f"/api/intelligent-evals/{eval_db.id}/decision-logs",
        json={
            "decision_type": "execute_session",
            "reason": "时段 8-10h 欠账 2 个会话",
            "context": {
                "current_slot": "8-10h",
                "deficit": 2,
                "completed_sessions": 1,
            },
            "cron_id": "cron-123",
        },
    )
    assert response.status_code == 200

    data = response.json()
    assert data["eval_id"] == eval_db.id
    assert data["decision_type"] == "execute_session"
    assert data["reason"] == "时段 8-10h 欠账 2 个会话"
    assert data["context"]["current_slot"] == "8-10h"
    assert data["cron_id"] == "cron-123"

    # Verify log saved to DB
    log = db_session.exec(
        select(IntelligentEvalDecisionLogDB).where(IntelligentEvalDecisionLogDB.eval_id == eval_db.id)
    ).first()
    assert log is not None
    assert log.decision_type == "execute_session"


def test_create_decision_log_eval_not_found(client: TestClient):
    """Test creating decision log for non-existent eval."""
    response = client.post(
        "/api/intelligent-evals/nonexistent/decision-logs",
        json={
            "decision_type": "wait",
            "reason": "test",
            "context": {},
            "cron_id": "cron-123",
        },
    )
    assert response.status_code == 404


def test_decision_log_multiple_entries(client: TestClient, db_session: Session):
    """Test creating multiple decision logs for same eval."""
    # Create eval
    eval_db = IntelligentEvalDB(
        name="test",
        target_id="target1",
        status=IntelligentEvalStatus.EXECUTING.value,
    )
    db_session.add(eval_db)
    db_session.commit()

    # Create 3 decision logs
    decisions = [
        ("execute_session", "时段到期"),
        ("wait", "当前时段无欠账"),
        ("start_analysis", "所有会话完成"),
    ]

    for decision_type, reason in decisions:
        response = client.post(
            f"/api/intelligent-evals/{eval_db.id}/decision-logs",
            json={
                "decision_type": decision_type,
                "reason": reason,
                "context": {},
                "cron_id": "cron-123",
            },
        )
        assert response.status_code == 200

    # Verify all logs saved
    logs = db_session.exec(
        select(IntelligentEvalDecisionLogDB)
        .where(IntelligentEvalDecisionLogDB.eval_id == eval_db.id)
        .order_by(IntelligentEvalDecisionLogDB.created_at)
    ).all()
    assert len(logs) == 3
    assert logs[0].decision_type == "execute_session"
    assert logs[1].decision_type == "wait"
    assert logs[2].decision_type == "start_analysis"
