"""商业账期边界与配额重置周期键。"""

from __future__ import annotations

import calendar
import hashlib
from datetime import UTC, datetime

from app.models.commercial import CommercialEntitlement, TenantSubscription


def quota_period_key_for(
    entitlement: CommercialEntitlement,
    subscription: TenantSubscription,
    occurred_at: datetime,
) -> str:
    occurred = _utc(occurred_at)
    if entitlement.reset_interval == "monthly":
        return occurred.strftime("%Y-%m")
    if entitlement.reset_interval == "quarterly":
        return f"{occurred.year}-Q{((occurred.month - 1) // 3) + 1}"
    if entitlement.reset_interval == "annual":
        return str(occurred.year)
    if entitlement.reset_interval == "contract":
        return f"contract-{_utc(subscription.starts_at).date().isoformat()}"
    return "all-time"


def period_key_for(
    entitlement: CommercialEntitlement,
    subscription: TenantSubscription,
    occurred_at: datetime,
) -> str:
    """兼容旧调用方；该键只表示权益重置周期，不再表示账期。"""

    return quota_period_key_for(entitlement, subscription, occurred_at)


def billing_period_key(
    tenant_id: str,
    subscription_id: str,
    period_start: datetime,
    period_end: datetime,
) -> str:
    canonical = ":".join(
        (
            tenant_id.strip(),
            subscription_id.strip(),
            _utc(period_start).isoformat(),
            _utc(period_end).isoformat(),
        )
    )
    return f"bp-{hashlib.sha256(canonical.encode('utf-8')).hexdigest()[:40]}"


def add_billing_interval(value: datetime, billing_interval: str) -> datetime:
    months = {"monthly": 1, "quarterly": 3, "annual": 12}.get(billing_interval)
    if months is None:
        raise ValueError("合同制账期不能自动推导下一周期，必须显式续签合同。")
    current = _utc(value)
    month_index = current.year * 12 + current.month - 1 + months
    target_year, zero_based_month = divmod(month_index, 12)
    target_month = zero_based_month + 1
    current_last_day = calendar.monthrange(current.year, current.month)[1]
    target_last_day = calendar.monthrange(target_year, target_month)[1]
    target_day = (
        target_last_day if current.day == current_last_day else min(current.day, target_last_day)
    )
    return current.replace(year=target_year, month=target_month, day=target_day)


def _utc(value: datetime) -> datetime:
    return value.replace(tzinfo=UTC) if value.tzinfo is None else value.astimezone(UTC)
