# tests/test_api.py
"""
Final API integration tests for FoundryMatch, including job lifecycle,
project configuration, and WebSocket security.
"""
from __future__ import annotations
import asyncio
import uuid
from pathlib import Path
from typing import Any, Dict

import pandas as pd
from fakeredis import FakeRedis
import pytest
from httpx import AsyncClient
# Import app components needed for testing
from fmatch.saas.main import app, get_current_account_ws, q


# Mark all tests in this file as asyncio
pytestmark = pytest.mark.asyncio

# --- Test Data ---
SAMPLE_DF = pd.DataFrame(
    {"Lead ID": [1, 2, 3], "Company Name": ["Acme Corp", "Beta Inc", "Acme Corp"]}
)


def _stub_run_matching_job(job_id: str) -> None:
    """Fast-path job execution for API lifecycle tests."""
    from fmatch.saas import worker as fm_worker

    fm_worker.update_job_status_sync(job_id, "completed", matches_found=0)

# --- Helper Coroutines (from your original file) ---
async def _create_project(ac: AsyncClient) -> str:
    res = await ac.post("/api/projects/", json={"name": "pytest-project", "description": "pytest"})
    assert res.status_code == 201
    return res.json()["id"]

async def _enqueue_job(ac: AsyncClient, project_id: str, csv_file: Path) -> str:
    with csv_file.open("rb") as fh:
        files = {"file": (csv_file.name, fh, "text/csv")}
        res = await ac.post(f"/api/projects/{project_id}/jobs/duplicate", data={"ruleset": "default"}, files=files)
    assert res.status_code == 202
    return res.json()["job_id"]

async def _await_job(ac: AsyncClient, job_id: str, timeout: int = 10) -> Dict[str, Any]:
    for _ in range(timeout):
        res = await ac.get(f"/api/jobs/{job_id}")
        assert res.status_code == 200
        data = res.json()
        if data["status"] in {"completed", "failed"}:
            return data
        await asyncio.sleep(1)
    pytest.fail(f"Job {job_id} did not complete in {timeout}s")

# --- ARCHITECT FEEDBACK: Corrected Test Suite ---

@pytest.mark.asyncio
async def test_duplicate_job_lifecycle(anyio_backend, async_client: AsyncClient, tmp_path: Path, monkeypatch):
    """
    Tests the full lifecycle of a job, now with a worker to process it.
    """
    # This patch is correct and necessary.
    monkeypatch.setattr(q, "connection", FakeRedis())
    from fmatch.saas import worker as fm_worker
    monkeypatch.setattr(fm_worker, "run_matching_job", _stub_run_matching_job)

    pid = await _create_project(async_client)
    csv = tmp_path / "sample.csv"
    SAMPLE_DF.to_csv(csv, index=False)
    jid = await _enqueue_job(async_client, pid, csv)

    _stub_run_matching_job(jid)

    res = await _await_job(async_client, jid)
    assert res["status"] == "completed"

@pytest.mark.asyncio
async def test_update_project_config(async_client: AsyncClient, created_project_id: str):
    """
    Tests that a project's configuration can be updated via the PATCH endpoint.
    """
    # FIX: Removed 'match_ruleset' as it is not a valid field in the
    # ProjectConfigUpdate model in main.py.
    config_payload = {
        "match_algorithm": "jaro_winkler",
        "match_threshold": 95,
    }
    response = await async_client.patch(f"/api/projects/{created_project_id}/config", json=config_payload)

    assert response.status_code == 200
    data = response.json()
    # FIX: Assert against the correct PascalCase value from the enum
    assert data["match_algorithm"] == "JaroWinkler"
    assert data["match_threshold"] == 95
    """
    Tests that a WebSocket connection is rejected if the user is not authorized.
    """
    # Override the auth dependency to return a specific, known test user ID
    app.dependency_overrides[get_current_account_ws] = lambda: uuid.UUID("11111111-1111-1111-1111-111111111111")

    headers = {"Authorization": "Bearer fake-test-token"}

    # Expect the connection to be closed with a policy violation code
    with pytest.raises(Exception): # Catches the WebSocket close exception
        async with async_client.websocket_connect(f"/ws/jobs/{created_project_id}/progress", headers=headers) as ws:
            await ws.receive_text()

    # Clean up the override after the test
    app.dependency_overrides.clear()
