"""Tests for the Forgejo Mirror Repo node."""

from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from fw_nodes_forgejo.nodes.mirror_repo import ForgejoMirrorRepoNode

CREDENTIAL_ID = "cred_forgejo_1"
CREDENTIAL_DATA = {
    "server_url": "https://forgejo.example.com",
    "api_token": "test-token-abc123",
}

FORGEJO_RESPONSE = {
    "id": 42,
    "full_name": "myorg/my-mirror",
    "html_url": "https://forgejo.example.com/myorg/my-mirror",
    "clone_url": "https://forgejo.example.com/myorg/my-mirror.git",
    "mirror": True,
}


@pytest.fixture
def node():
    return ForgejoMirrorRepoNode()


@pytest.fixture
def mock_context_with_creds(mock_context):
    mock_context._credentials[CREDENTIAL_ID] = CREDENTIAL_DATA
    return mock_context


def _make_inputs(**overrides):
    base = {
        "credential_id": CREDENTIAL_ID,
        "clone_url": "https://github.com/someuser/somerepo.git",
        "repo_name": "my-mirror",
        "repo_owner": "myorg",
        "description": "A mirrored repo",
        "private": False,
        "mirror_interval": "8h0m0s",
        "wiki": False,
        "issues": False,
        "labels": False,
        "pull_requests": False,
        "releases": False,
    }
    base.update(overrides)
    return base


def _mock_response(status_code=201, json_data=None, text=""):
    resp = MagicMock()
    resp.status_code = status_code
    resp.json.return_value = json_data or FORGEJO_RESPONSE
    resp.text = text
    return resp


def _patch_client(mock_response):
    mock_client = AsyncMock()
    mock_client.post.return_value = mock_response
    mock_client.__aenter__ = AsyncMock(return_value=mock_client)
    mock_client.__aexit__ = AsyncMock(return_value=False)
    return patch(
        "fw_nodes_forgejo.nodes.mirror_repo.httpx.AsyncClient",
        return_value=mock_client,
    ), mock_client


async def test_mirror_repo_success(node, mock_context_with_creds):
    resp = _mock_response()
    patcher, mock_client = _patch_client(resp)

    with patcher:
        result = await node.execute(_make_inputs(), mock_context_with_creds)

    assert result.success is True
    assert result.repo_id == 42
    assert result.full_name == "myorg/my-mirror"
    assert result.mirror is True

    call_kwargs = mock_client.post.call_args
    assert call_kwargs[0][0] == "https://forgejo.example.com/api/v1/repos/migrate"
    assert call_kwargs[1]["json"]["clone_addr"] == "https://github.com/someuser/somerepo.git"
    assert call_kwargs[1]["json"]["mirror"] is True
    assert call_kwargs[1]["json"]["repo_owner"] == "myorg"


async def test_mirror_repo_no_owner(node, mock_context_with_creds):
    """When repo_owner is empty, it should not be included in the payload."""
    resp = _mock_response()
    patcher, mock_client = _patch_client(resp)

    with patcher:
        await node.execute(_make_inputs(repo_owner=""), mock_context_with_creds)

    payload = mock_client.post.call_args[1]["json"]
    assert "repo_owner" not in payload


async def test_mirror_repo_api_error(node, mock_context_with_creds):
    resp = _mock_response(
        status_code=409,
        json_data={"message": "repository already exists"},
        text='{"message": "repository already exists"}',
    )
    patcher, _ = _patch_client(resp)

    with patcher:
        with pytest.raises(RuntimeError, match="repository already exists"):
            await node.execute(_make_inputs(), mock_context_with_creds)


async def test_mirror_repo_registers_secret(node, mock_context_with_creds):
    resp = _mock_response()
    patcher, _ = _patch_client(resp)

    with patcher:
        await node.execute(_make_inputs(), mock_context_with_creds)

    assert "test-token-abc123" in mock_context_with_creds._secrets


async def test_mirror_repo_credential_not_found(node, mock_context):
    with pytest.raises(ValueError, match="Credential .* not found"):
        await node.execute(_make_inputs(), mock_context)
