"""Convert CF / Cloud usage metrics into Cloud millicredits.

Rates are DB-configurable. Negative balances are allowed; this module only computes.
"""
from __future__ import annotations

from decimal import Decimal, ROUND_HALF_UP
from typing import Mapping

DISPLAY_SCALE = 1000

DEFAULT_RATES: dict[str, Decimal] = {
    "hosted_app_requests": Decimal("0.0001"),
    "hosted_app_egress_bytes": Decimal("0.000000001"),
    "cpu_minutes": Decimal("0.1"),
    "db_reads": Decimal("0.00001"),
    "db_writes": Decimal("0.0001"),
    "storage_gb": Decimal("1"),
    "job_executions": Decimal("1"),
    "ai_embed_tokens": Decimal("0.00001"),
    "ai_completion_tokens": Decimal("0.00003"),
    "ai_searches": Decimal("0.001"),
    "ext_queries": Decimal("0.001"),
}

METRIC_KEYS = tuple(DEFAULT_RATES.keys())

# Per-project surcharge defaults, used the first time a project's billing-extras row
# is created (src.domain.cloud_credits.repository.ProjectBillingExtrasRepository).
# Existing rows keep whatever was baked in at creation time if these change later.
DEFAULT_MARKUP_PCT = Decimal("0")
DEFAULT_FIXED_MONTHLY_FEE_CREDITS = Decimal("0")


def apply_project_markup_and_fee(
    metered_millicredits: int,
    *,
    markup_percentage: Decimal,
    fixed_monthly_fee_credits: Decimal,
    days_in_month: int,
) -> int:
    """Add a project's percentage markup and daily-prorated flat fee to its metered usage.

    Covers platform costs CF doesn't directly measure (support, generic overhead) —
    applied only to metered Cloud/backend service usage, not hosted-app traffic,
    since CF already measures hosting requests/egress directly. The fixed fee is
    per project per day (fee / days_in_month), not once per workspace, so a
    workspace's total scales with how many of its projects are active.
    """
    metered_credits = Decimal(metered_millicredits) / DISPLAY_SCALE
    with_markup = metered_credits * (1 + markup_percentage / 100)
    daily_fixed = fixed_monthly_fee_credits / days_in_month if days_in_month else Decimal("0")
    total = with_markup + daily_fixed
    if total <= 0:
        return 0
    return int((total * DISPLAY_SCALE).to_integral_value(rounding=ROUND_HALF_UP))


def millicredits_from_metrics(
    metrics: Mapping[str, float | int],
    rates: Mapping[str, Decimal] | None = None,
) -> int:
    """Return charged millicredits (1 credit = 1000 millicredits), never negative."""
    table = rates if rates is not None else DEFAULT_RATES
    total = Decimal("0")
    for key, rate in table.items():
        raw = metrics.get(key, 0) or 0
        total += Decimal(str(raw)) * Decimal(str(rate))
    if total <= 0:
        return 0
    return int((total * DISPLAY_SCALE).to_integral_value(rounding=ROUND_HALF_UP))


def credits_from_millicredits(millicredits: int) -> float:
    return millicredits / DISPLAY_SCALE
