"""Tests for the FastAPI daemon server."""

from datetime import datetime, timezone
from unittest.mock import AsyncMock, MagicMock, patch

import pytest
from fastapi.testclient import TestClient

from fliiq.runtime.scheduler.loader import save_job
from fliiq.runtime.scheduler.models import (
    JobDefinition,
    RunLogEntry,
    TriggerConfig,
)


def _make_job(name: str = "test-job", schedule: str = "0 9 * * *") -> JobDefinition:
    return JobDefinition(
        name=name,
        trigger=TriggerConfig(type="cron", schedule=schedule),
        prompt="Test prompt",
    )


@pytest.fixture()
def client(tmp_path, monkeypatch):
    """Create a TestClient with mocked LLM config and tmp project root.

    Yields (TestClient, auth_headers) — use auth_headers for /api/* routes.
    """
    jobs_dir = tmp_path / ".fliiq" / "jobs"
    jobs_dir.mkdir(parents=True)
    monkeypatch.setenv("FLIIQ_PROJECT_ROOT", str(tmp_path))
    monkeypatch.delenv("TELEGRAM_BOT_TOKEN", raising=False)

    with patch("fliiq.api.server.resolve_llm_config", return_value=MagicMock()), \
         patch("fliiq.runtime.config.resolve_env_file", return_value=None):
        from fliiq.api.server import app

        with TestClient(app) as c:
            secret = app.state.daemon_secret
            yield c, {"Authorization": f"Bearer {secret}"}


def test_health_endpoint(client):
    c, _auth = client
    response = c.get("/health")
    assert response.status_code == 200
    data = response.json()
    assert data["status"] == "ok"
    assert "jobs" in data
    assert "uptime_s" in data


def test_api_requires_auth(client):
    """API routes return 401 without valid Bearer token."""
    c, _auth = client
    assert c.get("/api/jobs").status_code == 401
    assert c.get("/api/jobs", headers={"Authorization": "Bearer wrong"}).status_code == 401


def test_list_jobs_empty(client):
    c, auth = client
    response = c.get("/api/jobs", headers=auth)
    assert response.status_code == 200
    assert response.json() == []


def test_list_jobs_with_jobs(client, tmp_path):
    c, auth = client
    jobs_dir = tmp_path / ".fliiq" / "jobs"
    save_job(_make_job("job-aa"), jobs_dir)
    save_job(_make_job("job-bb"), jobs_dir)

    response = c.get("/api/jobs", headers=auth)
    assert response.status_code == 200
    data = response.json()
    assert len(data) == 2
    names = {j["name"] for j in data}
    assert names == {"job-aa", "job-bb"}


def test_trigger_job(client, tmp_path):
    c, auth = client
    jobs_dir = tmp_path / ".fliiq" / "jobs"
    save_job(_make_job("run-me"), jobs_dir)

    mock_entry = RunLogEntry(
        job_name="run-me",
        started_at=datetime.now(timezone.utc),
        completed_at=datetime.now(timezone.utc),
        status="success",
        duration_ms=100,
        iterations=2,
        stop_reason="end_turn",
    )

    with patch("fliiq.api.server.execute_job", new_callable=AsyncMock, return_value=mock_entry):
        response = c.post("/api/jobs/run-me/run", headers=auth)

    assert response.status_code == 200
    data = response.json()
    assert data["job_name"] == "run-me"
    assert data["status"] == "success"


def test_trigger_nonexistent_job(client):
    c, auth = client
    response = c.post("/api/jobs/ghost/run", headers=auth)
    assert response.status_code == 404
