"""Billing data models for CHAKRA plan integration.

Provides plan information, status tracking, and credit transaction models
for enforcing subscription requirements in AiCippy.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from datetime import UTC, datetime
from enum import StrEnum
from typing import Final

from aicippy.config import get_settings

# Cache TTL for plan validation (seconds)
PLAN_CACHE_TTL_SECONDS: Final[int] = 300  # 5 minutes


class PlanStatus(StrEnum):
    """Subscription plan status values from AiVibe platform."""

    ACTIVE = "active"
    EXPIRED = "expired"
    CANCELLED = "cancelled"
    SUSPENDED = "suspended"
    NO_PLAN = "no_plan"


@dataclass(frozen=True, slots=True)
class PlanInfo:
    """Immutable plan information for a user.

    Attributes:
        tenant_id: AiVibe tenant ID (vk_xxx).
        org_tenant_id: Organization tenant ID that this user belongs to
            (e.g. "org_aivibe"). Empty string if user is not part of any org.
        email: User email address.
        plan: Plan tier name (aarambh/vajra/chakra).
        status: Current plan status.
        credits_remaining: Credits available for use.
        credits_total: Total monthly credits allocation.
        expiry: Subscription period end date (ISO 8601).
        is_admin: Whether user is an admin (bypasses all checks).
        fetched_at: When this plan info was fetched from platform API.

    """

    tenant_id: str
    org_tenant_id: str
    email: str
    plan: str
    status: PlanStatus
    credits_remaining: int
    credits_total: int
    expiry: datetime | None
    is_admin: bool
    fetched_at: datetime = field(default_factory=lambda: datetime.now(UTC))

    @property
    def is_valid_for_aicippy(self) -> bool:
        """Check if the user can use AiCippy.

        Valid if:
        - Admin user (bypasses all restrictions), OR
        - Plan matches required_plan setting AND status is active AND not expired AND credits > 0
        """
        if self.is_admin:
            return True

        if self.plan not in (get_settings().required_plan, "admin"):
            return False

        if self.status != PlanStatus.ACTIVE:
            return False

        if self.expiry is not None:
            now = datetime.now(UTC)
            if self.expiry < now:
                return False

        return self.credits_remaining > 0

    @property
    def is_cache_stale(self) -> bool:
        """Check if cached plan info is older than TTL."""
        elapsed = (datetime.now(UTC) - self.fetched_at).total_seconds()
        return elapsed > PLAN_CACHE_TTL_SECONDS

    @property
    def denial_reason(self) -> str | None:  # noqa: PLR0911
        """Get a human-readable reason why the user cannot use AiCippy.

        Returns None if the user is valid.
        """
        if self.is_admin:
            return None

        required = get_settings().required_plan
        plan_label = required.upper()

        if self.status == PlanStatus.NO_PLAN:
            return f"No active subscription found. Subscribe to the {plan_label} plan at vibekaro.ai/pricing"

        if self.plan not in (required, "admin"):
            return (
                f"Your current plan ({self.plan.upper()}) does not include AiCippy access. "
                f"Upgrade to the {plan_label} plan at vibekaro.ai/pricing"
            )

        if self.status == PlanStatus.CANCELLED:
            return f"Your {plan_label} subscription has been cancelled. Resubscribe at vibekaro.ai/pricing"

        if self.status == PlanStatus.SUSPENDED:
            return f"Your {plan_label} subscription is suspended. Contact support at vibekaro.ai/pricing"

        if self.status == PlanStatus.EXPIRED:
            return f"Your {plan_label} subscription has expired. Renew at vibekaro.ai/pricing"

        if self.expiry is not None and self.expiry < datetime.now(UTC):
            return f"Your {plan_label} subscription period has ended. Renew at vibekaro.ai/pricing"

        if self.credits_remaining <= 0:
            return "You have used all 250 monthly credits. Credits reset at the start of your next billing cycle."

        return None


@dataclass(frozen=True, slots=True)
class CreditTransaction:
    """Result of a credit deduction operation.

    Attributes:
        success: Whether the deduction succeeded.
        credits_remaining: Credits left after deduction.
        transaction_id: Unique transaction ID for audit trail.
        error: Error message if deduction failed.

    """

    success: bool
    credits_remaining: int
    transaction_id: str | None = None
    error: str | None = None
