import os import time from dataclasses import dataclass from typing import Optional @dataclass(frozen=True) class LicenseDecision: valid: bool valid_until: Optional[int] status: str # "active"|"grace"|"expired"|"none" def compute_license_decision(current_period_end: Optional[int], status: Optional[str]) -> LicenseDecision: """ current_period_end: epoch seconds (UTC) status: normalized db status or stripe subscription status (best effort) """ now = int(time.time()) if not current_period_end: return LicenseDecision(valid=False, valid_until=None, status="none") grace_days = int(os.getenv("AZA_GRACE_DAYS", "0")) grace_seconds = grace_days * 24 * 60 * 60 # Normalize status (kept for future; time gating is primary) _ = (status or "").lower().strip() if now <= int(current_period_end): return LicenseDecision(valid=True, valid_until=int(current_period_end), status="active") if grace_seconds > 0 and now <= int(current_period_end) + grace_seconds: return LicenseDecision(valid=True, valid_until=int(current_period_end), status="grace") return LicenseDecision(valid=False, valid_until=int(current_period_end), status="expired")