from types import SimpleNamespace
from unittest.mock import AsyncMock
from uuid import uuid4

import pytest
from src.workers.tasks.env_sync import _with_platform_env_vars


def test_platform_env_injects_cloud_service_token_as_secret(monkeypatch):
    monkeypatch.setattr(
        "src.config.get_settings",
        lambda: SimpleNamespace(
            cloud_public_api_url="https://cloud.example",
            cloud_control_api_url="",
            api_url="https://api.example",
        ),
    )
    out = _with_platform_env_vars(
        [{"key": "CUSTOM", "value": "x", "visibility": "secret", "environment": "all"}],
        project_id="proj-1",
        client_id="cli_ref",
        service_token="scoped-token",
    )
    by_key = {row["key"]: row for row in out}
    assert by_key["CLOUD_SERVICE_TOKEN"]["visibility"] == "secret"
    assert by_key["CLOUD_SERVICE_TOKEN"]["value"] == "scoped-token"
    assert by_key["CLOUD_GATEWAY_URL"]["value"] == "https://cloud.example"
    assert by_key["CLOUD_GATEWAY_URL"]["visibility"] == "secret"
    assert "NUXT_PUBLIC_P2HS_GATEWAY_URL" not in by_key
    assert "VITE_P2HS_GATEWAY_URL" not in by_key
    assert "NUXT_PUBLIC_P2HS_CORE_PUBLIC_URL" not in by_key
    assert "VITE_P2HS_CORE_PUBLIC_URL" not in by_key
    assert by_key["P2HS_CORE_URL"]["visibility"] == "secret"
    assert by_key["PROJECT_ID"]["value"] == "proj-1"
    assert by_key["CUSTOM"]["value"] == "x"
    assert "scoped-token" not in {
        row["value"] for row in out if row["visibility"] == "public"
    }


@pytest.mark.asyncio
async def test_ensure_token_reuses_existing_without_repush():
    from src.core.security.encryption import encrypt_value
    from src.domain.edge_functions.runtime_token import ensure_project_cloud_service_token

    tokens = AsyncMock()
    tokens.get.return_value = SimpleNamespace(token_enc=encrypt_value("already"))
    cloud = AsyncMock()
    token = await ensure_project_cloud_service_token(tokens, cloud, uuid4())
    assert token == "already"
    tokens.create.assert_not_called()
    cloud.push_function_token.assert_not_called()


@pytest.mark.asyncio
async def test_ensure_token_repush_when_requested():
    from src.core.security.encryption import encrypt_value
    from src.domain.edge_functions.runtime_token import ensure_project_cloud_service_token

    tokens = AsyncMock()
    tokens.get.return_value = SimpleNamespace(token_enc=encrypt_value("already"))
    cloud = AsyncMock()
    project_id = uuid4()
    token = await ensure_project_cloud_service_token(
        tokens, cloud, project_id, always_push=True
    )
    assert token == "already"
    tokens.create.assert_not_called()
    cloud.push_function_token.assert_awaited_once_with(
        project_id=str(project_id), token="already"
    )


@pytest.mark.asyncio
async def test_ensure_token_survives_missing_cloud_function_token_route():
    from src.domain.edge_functions.runtime_token import ensure_project_cloud_service_token

    tokens = AsyncMock()
    tokens.get.return_value = None
    cloud = AsyncMock()
    error = RuntimeError("not_found")
    error.response = SimpleNamespace(status_code=404)
    cloud.push_function_token.side_effect = error
    token = await ensure_project_cloud_service_token(tokens, cloud, uuid4(), always_push=True)
    assert token
    tokens.create.assert_awaited_once()


@pytest.mark.asyncio
async def test_pages_retry_skips_missing_branch_deployments():
    from src.domain.hosting.adapters.cloudflare_pages import CloudflarePagesProvider

    provider = CloudflarePagesProvider()
    provider.find_latest_live_deployment_for_branch = AsyncMock(return_value=None)
    provider._request = AsyncMock()
    retried = await provider.retry_latest_branch_deployments("p1q9l8dk", ("preview", "main"))
    assert retried == []
    provider._request.assert_not_called()


@pytest.mark.asyncio
async def test_pages_retry_failure_does_not_fail_env_push():
    from src.workers.tasks.env_sync import _push_to_pages

    adapter = AsyncMock()
    adapter.sync_to_hosting = AsyncMock()
    adapter.retry_runtime_deployments = AsyncMock(side_effect=RuntimeError("direct upload"))
    await _push_to_pages(adapter, "p1q9l8dk", [], "proj-1")
    adapter.sync_to_hosting.assert_awaited_once()
