"""Integration tests for cron pool API."""

from unittest.mock import AsyncMock, MagicMock, patch

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

from agenteval.intelligent_eval.openclaw_client import OpenClawClient
from agenteval.storage.db import 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 OpenClawCronPoolDB  # noqa: F401

    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]())


@pytest.fixture()
def mock_openclaw_client():
    """Mock OpenClaw client."""
    client = MagicMock(spec=OpenClawClient)
    
    counter = {"value": 0}
    
    async def create_cron_impl(**kwargs):
        counter["value"] += 1
        return f"cron-{counter['value']}"
    
    client.create_cron = AsyncMock(side_effect=create_cron_impl)
    client.delete_cron = AsyncMock()
    client.list_crons = AsyncMock(return_value=[])
    return client


def test_get_cron_pool_status_empty(client: TestClient):
    """Test getting pool status when pool is empty."""
    response = client.get("/api/openclaw/cron-pool")
    assert response.status_code == 200

    data = response.json()
    assert data["pool"]["total"] == 0
    assert data["pool"]["idle"] == 0
    assert data["pool"]["busy"] == 0
    assert data["pool"]["stuck"] == 0
    assert data["pool"]["min_size"] == 5
    assert data["pool"]["max_size"] == 20


def test_get_cron_pool_status_with_crons(client: TestClient, db_session: Session):
    """Test getting pool status with existing crons."""
    # Create crons
    for i in range(10):
        status = ["idle", "busy", "stuck"][i % 3]
        cron = OpenClawCronPoolDB(
            openclaw_cron_id=f"cron-{i}",
            status=status,
            last_active_at=utc_now(),
        )
        db_session.add(cron)
    db_session.commit()

    response = client.get("/api/openclaw/cron-pool")
    assert response.status_code == 200

    data = response.json()
    assert data["pool"]["total"] == 10
    assert data["pool"]["idle"] == 4
    assert data["pool"]["busy"] == 3
    assert data["pool"]["stuck"] == 3


@patch("agenteval.web.routers.openclaw_cron_pool.OpenClawClient")
def test_scale_up_pool(mock_client_class, client: TestClient, db_session: Session, mock_openclaw_client):
    """Test manually scaling up the pool."""
    mock_client_class.return_value = mock_openclaw_client

    # Create 5 crons
    for i in range(5):
        cron = OpenClawCronPoolDB(
            openclaw_cron_id=f"existing-cron-{i}",
            status="idle",
            last_active_at=utc_now(),
        )
        db_session.add(cron)
    db_session.commit()

    # Scale up to 10
    response = client.post("/api/openclaw/cron-pool/scale", json={"target_size": 10})
    assert response.status_code == 200

    data = response.json()
    assert data["success"] is True
    assert data["scaled_up"] == 5
    assert data["current_size"] == 10

    # Verify crons created
    from sqlmodel import select

    crons = db_session.exec(select(OpenClawCronPoolDB)).all()
    assert len(crons) == 10


@patch("agenteval.web.routers.openclaw_cron_pool.OpenClawClient")
def test_scale_down_pool(mock_client_class, client: TestClient, db_session: Session, mock_openclaw_client):
    """Test manually scaling down the pool."""
    mock_client_class.return_value = mock_openclaw_client

    # Create 15 idle crons
    for i in range(15):
        cron = OpenClawCronPoolDB(
            openclaw_cron_id=f"cron-{i}",
            status="idle",
            last_active_at=utc_now(),
        )
        db_session.add(cron)
    db_session.commit()

    # Scale down to 8
    response = client.post("/api/openclaw/cron-pool/scale", json={"target_size": 8})
    assert response.status_code == 200

    data = response.json()
    assert data["success"] is True
    assert data["scaled_down"] == 7
    assert data["current_size"] == 8

    # Verify crons deleted
    from sqlmodel import select

    crons = db_session.exec(select(OpenClawCronPoolDB)).all()
    assert len(crons) == 8


@patch("agenteval.web.routers.openclaw_cron_pool.OpenClawClient")
def test_scale_pool_no_change(mock_client_class, client: TestClient, db_session: Session):
    """Test scaling pool to same size (no change)."""
    mock_client_class.return_value = MagicMock()

    # Create 10 crons
    for i in range(10):
        cron = OpenClawCronPoolDB(
            openclaw_cron_id=f"cron-{i}",
            status="idle",
            last_active_at=utc_now(),
        )
        db_session.add(cron)
    db_session.commit()

    # Scale to same size
    response = client.post("/api/openclaw/cron-pool/scale", json={"target_size": 10})
    assert response.status_code == 200

    data = response.json()
    assert data["success"] is True
    assert data["current_size"] == 10
    assert "already at target size" in data["message"]
