"""roboco.services.github_provisioning — repo creation against MockTransport."""

from __future__ import annotations

import json
from collections.abc import Callable

import httpx
import pytest
from roboco.config import settings
from roboco.services.github_provisioning import (
    GitHubProvisioningService,
    ProvisioningDisabledError,
    ProvisioningError,
)

Handler = Callable[[httpx.Request], httpx.Response]


def _client(handler: Handler) -> httpx.AsyncClient:
    return httpx.AsyncClient(transport=httpx.MockTransport(handler))


@pytest.mark.asyncio
async def test_disabled_when_no_token(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setattr(settings, "provisioning_enabled", True)
    svc = GitHubProvisioningService(
        token="", org="", client=_client(lambda _r: httpx.Response(201))
    )
    assert svc.enabled is False
    with pytest.raises(ProvisioningDisabledError):
        await svc.create_repo("x")


@pytest.mark.asyncio
async def test_disabled_when_master_switch_off(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setattr(settings, "provisioning_enabled", False)
    svc = GitHubProvisioningService(
        token="tok", org="acme", client=_client(lambda _r: httpx.Response(201))
    )
    assert svc.enabled is False


@pytest.mark.asyncio
async def test_create_repo_success(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setattr(settings, "provisioning_enabled", True)

    def handler(request: httpx.Request) -> httpx.Response:
        assert request.url.path == "/orgs/acme/repos"
        body = json.loads(request.content)
        assert body["name"] == "newrepo"
        assert body["auto_init"] is True
        assert body["private"] is True
        return httpx.Response(
            201,
            json={
                "full_name": "acme/newrepo",
                "clone_url": "https://github.com/acme/newrepo.git",
                "html_url": "https://github.com/acme/newrepo",
            },
        )

    svc = GitHubProvisioningService(token="tok", org="acme", client=_client(handler))
    assert svc.enabled is True
    repo = await svc.create_repo("newrepo", "desc")
    assert repo.full_name == "acme/newrepo"
    assert repo.clone_url.endswith("newrepo.git")


@pytest.mark.asyncio
async def test_create_repo_failure_raises(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setattr(settings, "provisioning_enabled", True)
    svc = GitHubProvisioningService(
        token="tok",
        org="acme",
        client=_client(lambda _r: httpx.Response(422, text="name exists")),
    )
    with pytest.raises(ProvisioningError):
        await svc.create_repo("dup")


@pytest.mark.asyncio
async def test_network_error_raises(monkeypatch: pytest.MonkeyPatch) -> None:
    monkeypatch.setattr(settings, "provisioning_enabled", True)

    def handler(_request: httpx.Request) -> httpx.Response:
        raise httpx.ConnectError("down")

    svc = GitHubProvisioningService(token="tok", org="acme", client=_client(handler))
    with pytest.raises(ProvisioningError):
        await svc.create_repo("x")


@pytest.mark.asyncio
async def test_create_repo_reuses_already_existing_repo(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    """#83/#84: a 422 'name already exists' (a repo orphaned by a rolled-back
    prior approval) is fetched and returned, not errored — so re-approval reuses
    the GitHub repo instead of colliding."""
    monkeypatch.setattr(settings, "provisioning_enabled", True)
    calls: list[str] = []

    def handler(request: httpx.Request) -> httpx.Response:
        calls.append(request.url.path)
        if request.url.path == "/orgs/acme/repos":
            return httpx.Response(
                422, text='{"message": "name already exists on this account"}'
            )
        if request.url.path == "/repos/acme/orphan":
            return httpx.Response(
                200,
                json={
                    "full_name": "acme/orphan",
                    "clone_url": "https://github.com/acme/orphan.git",
                    "html_url": "https://github.com/acme/orphan",
                },
            )
        return httpx.Response(404)

    svc = GitHubProvisioningService(token="tok", org="acme", client=_client(handler))
    repo = await svc.create_repo("orphan", "desc")
    assert repo.full_name == "acme/orphan"
    assert repo.clone_url.endswith("orphan.git")
    # POST create -> 422, then GET the existing repo.
    assert calls == ["/orgs/acme/repos", "/repos/acme/orphan"]


@pytest.mark.asyncio
async def test_create_repo_other_422_still_raises(
    monkeypatch: pytest.MonkeyPatch,
) -> None:
    """A 422 that is NOT the 'already exists' shape (e.g. a validation error) is
    still a hard failure — only the orphaned-repo case is treated idempotently."""
    monkeypatch.setattr(settings, "provisioning_enabled", True)
    svc = GitHubProvisioningService(
        token="tok",
        org="acme",
        client=_client(lambda _r: httpx.Response(422, text="name reserved")),
    )
    with pytest.raises(ProvisioningError):
        await svc.create_repo("dup")
