from __future__ import annotations

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

import pytest

from src.domain.cloud_credits.conversion import millicredits_from_metrics
from src.domain.cloud_credits.wallet import CloudCreditWallet


def test_conversion_uses_configurable_rates() -> None:
    millicredits = millicredits_from_metrics(
        {"hosted_app_requests": 10_000, "job_executions": 2},
        {"hosted_app_requests": Decimal("0.0001"), "job_executions": Decimal("1")},
    )
    # 10_000 * 0.0001 + 2 * 1 = 3.0 credits = 3000 millicredits
    assert millicredits == 3000


def test_conversion_zero_when_rates_are_zero() -> None:
    assert millicredits_from_metrics({"hosted_app_requests": 999}, {"hosted_app_requests": Decimal("0")}) == 0


@pytest.mark.asyncio
async def test_wallet_allows_negative_balance() -> None:
    workspace_id = uuid4()
    ledger = SimpleNamespace(
        acquire_lock=AsyncMock(),
        list_by_source=AsyncMock(return_value=[]),
        add_entry=AsyncMock(),
        sum_balance=AsyncMock(return_value=-2500),
    )
    wallet = CloudCreditWallet(ledger)
    settlement = await wallet.settle_usage(
        workspace_id=workspace_id,
        snapshot_date="2026-09-07",
        millicredits=2500,
        metrics={"job_executions": 3},
    )
    assert settlement.already_settled is False
    assert settlement.millicredits == 2500
    ledger.add_entry.assert_awaited()
    assert await wallet.balance_millicredits(workspace_id) == -2500


@pytest.mark.asyncio
async def test_jobs_allowed_when_fair_usage() -> None:
    from src.domain.cloud_credits.service import CloudCreditService

    svc = CloudCreditService.__new__(CloudCreditService)
    svc._policy = SimpleNamespace(get=AsyncMock(return_value=None))
    assert await CloudCreditService.jobs_allowed(svc, uuid4()) is True


@pytest.mark.asyncio
async def test_jobs_denied_when_capped_and_exhausted() -> None:
    from src.domain.cloud_credits.service import CloudCreditService

    svc = CloudCreditService.__new__(CloudCreditService)
    svc._policy = SimpleNamespace(
        get=AsyncMock(return_value=SimpleNamespace(enforcement_enabled=True, cap_credits=100))
    )
    svc._wallet = SimpleNamespace(balance_millicredits=AsyncMock(return_value=0))
    assert await CloudCreditService.jobs_allowed(svc, uuid4()) is False
