"""Tests for webhook receiver endpoint."""

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_webhook_job(name: str, source: str, match: dict | None = None) -> JobDefinition:
    return JobDefinition(
        name=name,
        trigger=TriggerConfig(type="webhook", source=source, match=match),
        prompt="Handle {{event.type}}",
    )


@pytest.fixture()
def client(tmp_path, monkeypatch):
    """TestClient with mocked LLM config.

    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_webhook_no_match(client):
    c, auth = client
    response = c.post("/api/webhooks/unknown-source", json={"event": "push"}, headers=auth)
    assert response.status_code == 200
    assert response.json()["matched"] == 0


def test_webhook_matches_source(client, tmp_path):
    c, auth = client
    jobs_dir = tmp_path / ".fliiq" / "jobs"
    save_job(_make_webhook_job("wh-job", "github"), jobs_dir)

    mock_entry = RunLogEntry(
        job_name="wh-job",
        started_at=datetime.now(timezone.utc),
        completed_at=datetime.now(timezone.utc),
        status="success",
        duration_ms=50,
    )

    with patch("fliiq.api.webhooks.router.execute_job", new_callable=AsyncMock, return_value=mock_entry):
        response = c.post("/api/webhooks/github", json={"event": "push"}, headers=auth)

    assert response.status_code == 200
    data = response.json()
    assert data["matched"] == 1
    assert data["results"][0]["status"] == "success"


def test_webhook_match_filter(client, tmp_path):
    c, auth = client
    jobs_dir = tmp_path / ".fliiq" / "jobs"
    save_job(_make_webhook_job("push-only", "github", match={"event.type": "push"}), jobs_dir)

    mock_entry = RunLogEntry(
        job_name="push-only",
        started_at=datetime.now(timezone.utc),
        completed_at=datetime.now(timezone.utc),
        status="success",
        duration_ms=50,
    )

    # Miss — wrong event type
    with patch("fliiq.api.webhooks.router.execute_job", new_callable=AsyncMock, return_value=mock_entry) as mock_exec:
        response = c.post("/api/webhooks/github", json={"event": {"type": "pr"}}, headers=auth)
    assert response.json()["matched"] == 0
    mock_exec.assert_not_called()

    # Hit — correct event type
    with patch("fliiq.api.webhooks.router.execute_job", new_callable=AsyncMock, return_value=mock_entry):
        response = c.post("/api/webhooks/github", json={"event": {"type": "push"}}, headers=auth)
    assert response.json()["matched"] == 1


def test_webhook_payload_passed(client, tmp_path):
    c, auth = client
    jobs_dir = tmp_path / ".fliiq" / "jobs"
    save_job(_make_webhook_job("payload-job", "stripe"), jobs_dir)

    mock_entry = RunLogEntry(
        job_name="payload-job",
        started_at=datetime.now(timezone.utc),
        completed_at=datetime.now(timezone.utc),
        status="success",
        duration_ms=50,
    )

    payload = {"charge": {"id": "ch_123", "amount": 5000}}

    with patch("fliiq.api.webhooks.router.execute_job", new_callable=AsyncMock, return_value=mock_entry) as mock_exec:
        c.post("/api/webhooks/stripe", json=payload, headers=auth)

    mock_exec.assert_called_once()
    call_kwargs = mock_exec.call_args
    assert call_kwargs.kwargs.get("payload") == payload or call_kwargs[0][4] == payload
