"""Connecting a website source auto-starts a build for the whole tenant --
pairing has no meaning scoped to one source, so there is no narrower job to
start. This must never make the source-connect call itself fail or look
like it didn't save, whatever happens to the auto-triggered build."""
from unittest.mock import AsyncMock, patch

import pytest
from fastapi import FastAPI, HTTPException
from fastapi.testclient import TestClient

from app.api import sources as sources_api
from app.services.infra import jobs

PAYLOAD = {"tenant_id": "org_x", "url": "https://shop.example.com"}


@pytest.fixture
def client():
    app = FastAPI()
    app.include_router(sources_api.router)
    return TestClient(app)


@pytest.fixture(autouse=True)
def no_real_source_writes():
    with patch.object(sources_api, "upsert_source"), \
         patch.object(sources_api, "assert_safe_url"):
        yield


def test_connecting_a_website_auto_starts_a_build(client):
    with patch.object(sources_api, "start_job", AsyncMock(return_value="job_123")) as start:
        resp = client.post("/sources/website", json=PAYLOAD)

    assert resp.status_code == 200
    body = resp.json()
    assert body["job_id"] == "job_123"
    assert body["job_status"] == "queued"
    assert body["kind"] == "crawl"
    start.assert_awaited_once()
    assert start.await_args.args[0] == "org_x"
    assert start.await_args.args[1] == "build"


def test_auto_build_false_skips_it_entirely(client):
    with patch.object(sources_api, "start_job", AsyncMock()) as start:
        resp = client.post("/sources/website", json={**PAYLOAD, "auto_build": False})

    assert resp.status_code == 200
    body = resp.json()
    assert "job_id" not in body
    assert "job_status" not in body
    start.assert_not_awaited()


def test_an_already_running_build_is_surfaced_not_swallowed(client):
    with patch.object(sources_api, "start_job",
                      AsyncMock(side_effect=jobs.JobAlreadyRunning("job_456"))):
        resp = client.post("/sources/website", json=PAYLOAD)

    assert resp.status_code == 200
    body = resp.json()
    assert body["job_id"] == "job_456"
    assert body["job_status"] == "already_running_without_this_source"
    # The source connection itself must still read as successful.
    assert body["status"] == "active"


def test_a_job_scheduling_failure_does_not_fail_the_source_connect(client):
    with patch.object(sources_api, "start_job",
                      AsyncMock(side_effect=HTTPException(status_code=503, detail="down"))):
        resp = client.post("/sources/website", json=PAYLOAD)

    assert resp.status_code == 200
    body = resp.json()
    assert body["job_status"] == "not_started"
    assert "job_id" not in body
    assert body["status"] == "active"
