from datetime import UTC, datetime
from types import SimpleNamespace
from unittest.mock import AsyncMock
from uuid import uuid4

import pytest
from src.core.exceptions import NotFoundError
from src.core.security.encryption import encrypt_value
from src.domain.edge_functions.connector_bindings import resolve_connector_bindings


def _connector_row(connector_id):
    return SimpleNamespace(id=connector_id)


def _connection_row(*, connector_id, status="active", config=None, secrets=None, created_at=None):
    return SimpleNamespace(
        connector_id=connector_id,
        status=status,
        config_json=config or {},
        secrets_enc=secrets or {},
        created_at=created_at or datetime.now(UTC),
    )


@pytest.mark.asyncio
async def test_resolves_config_and_secrets_with_prefixed_binding_names() -> None:
    connector_id = uuid4()
    repo = AsyncMock()
    repo.get_connector_by_key.return_value = _connector_row(connector_id)
    repo.list_connections_for_project.return_value = [
        _connection_row(
            connector_id=connector_id,
            config={"instance_url": "https://x.my.salesforce.com"},
            secrets={"access_token": encrypt_value("tok-123")},
        )
    ]

    env_vars, secrets = await resolve_connector_bindings(repo, uuid4(), ["salesforce"])

    assert env_vars == {"SALESFORCE_INSTANCE_URL": "https://x.my.salesforce.com"}
    assert secrets == {"SALESFORCE_ACCESS_TOKEN": "tok-123"}


@pytest.mark.asyncio
async def test_raises_when_no_active_connection_configured() -> None:
    repo = AsyncMock()
    repo.get_connector_by_key.return_value = _connector_row(uuid4())
    repo.list_connections_for_project.return_value = []

    with pytest.raises(NotFoundError, match="Stripe"):
        await resolve_connector_bindings(repo, uuid4(), ["stripe"])


@pytest.mark.asyncio
async def test_ignores_disconnected_connections() -> None:
    connector_id = uuid4()
    repo = AsyncMock()
    repo.get_connector_by_key.return_value = _connector_row(connector_id)
    repo.list_connections_for_project.return_value = [
        _connection_row(connector_id=connector_id, status="disconnected"),
    ]

    with pytest.raises(NotFoundError):
        await resolve_connector_bindings(repo, uuid4(), ["stripe"])


@pytest.mark.asyncio
async def test_picks_oldest_active_connection_when_multiple_exist() -> None:
    connector_id = uuid4()
    older = _connection_row(
        connector_id=connector_id,
        secrets={"api_key": encrypt_value("older-key")},
        created_at=datetime(2026, 1, 1, tzinfo=UTC),
    )
    newer = _connection_row(
        connector_id=connector_id,
        secrets={"api_key": encrypt_value("newer-key")},
        created_at=datetime(2026, 2, 1, tzinfo=UTC),
    )
    repo = AsyncMock()
    repo.get_connector_by_key.return_value = _connector_row(connector_id)
    repo.list_connections_for_project.return_value = [newer, older]

    _, secrets = await resolve_connector_bindings(repo, uuid4(), ["resend"])

    assert secrets == {"RESEND_API_KEY": "older-key"}
