"""
Quota enforcement system for DeepSweep CLI.

Free tier: 3 validations per month
Paid tiers (Starter/Pro): Unlimited validations

Features:
- Device fingerprinting (no auth required)
- API-first with local fallback
- Smart offline handling for paid users
- Monthly quota reset
"""

import contextlib
import hashlib
import json
import os
import platform
from dataclasses import asdict, dataclass
from datetime import date, datetime
from pathlib import Path
from typing import Literal


@dataclass
class QuotaStatus:
    """Current quota status for device."""

    device_id: str
    validations_this_month: int
    quota_limit: int
    tier: Literal["free", "starter", "pro"]
    last_validation_date: str | None
    last_reset_month: str  # YYYY-MM format
    quota_exceeded: bool
    has_api_key: bool
    is_offline: bool

    def to_dict(self) -> dict:
        """Convert to dictionary for JSON serialization."""
        return asdict(self)

    @classmethod
    def from_dict(cls, data: dict) -> "QuotaStatus":
        """Create from dictionary (JSON deserialization)."""
        return cls(**{k: v for k, v in data.items() if k in cls.__dataclass_fields__})


class QuotaEnforcer:
    """
    Manage validation quota enforcement.

    Stores quota data in ~/.config/deepsweep/quota.json
    """

    CONFIG_DIR = Path.home() / ".config" / "deepsweep"
    QUOTA_FILE = "quota.json"

    # Quota limits
    FREE_TIER_MONTHLY_LIMIT = 3
    PAID_TIER_UNLIMITED = 999999

    def __init__(self, offline_mode: bool = False):
        """
        Initialize quota enforcer.

        Args:
            offline_mode: If True, skip API calls (local fallback only)
        """
        self.config_dir = self.CONFIG_DIR
        self.quota_file = self.config_dir / self.QUOTA_FILE
        self.offline_mode = offline_mode or os.environ.get("DEEPSWEEP_OFFLINE") == "1"
        self._ensure_config_dir()

    def _ensure_config_dir(self) -> None:
        """Create config directory if it doesn't exist."""
        self.config_dir.mkdir(parents=True, exist_ok=True)

    def get_device_id(self) -> str:
        """
        Generate stable device fingerprint.

        Uses platform-specific identifiers to create a consistent hash
        that persists across sessions but is anonymous.

        Returns:
            SHA256 hash of device identifiers
        """
        # Combine multiple platform identifiers for stability
        identifiers = [
            platform.node(),  # Hostname
            platform.machine(),  # Machine type (x86_64, arm64, etc.)
            platform.processor(),  # Processor name
            os.environ.get("USER", os.environ.get("USERNAME", "unknown")),  # Username
        ]

        # Create deterministic hash
        fingerprint = "|".join(str(i) for i in identifiers)
        return hashlib.sha256(fingerprint.encode()).hexdigest()

    def _get_api_key(self) -> str | None:
        """Get API key from environment if set."""
        return os.environ.get("DEEPSWEEP_API_KEY")

    def _load_local_quota(self) -> QuotaStatus | None:
        """Load quota data from local storage."""
        if not self.quota_file.exists():
            return None

        try:
            with self.quota_file.open() as f:
                data = json.load(f)
            return QuotaStatus.from_dict(data)
        except (json.JSONDecodeError, KeyError, OSError):
            return None

    def _save_local_quota(self, status: QuotaStatus) -> None:
        """Save quota data to local storage."""
        try:
            with self.quota_file.open("w") as f:
                json.dump(status.to_dict(), f, indent=2)
        except OSError:
            # Fail silently if we can't write
            pass

    def _check_api_quota(self, _device_id: str, _api_key: str | None) -> QuotaStatus | None:
        """
        Check quota via API.

        Args:
            _device_id: Device fingerprint (unused until API implemented)
            _api_key: Optional API key for paid tiers (unused until API implemented)

        Returns:
            QuotaStatus from API, or None if API unavailable
        """
        if self.offline_mode:
            return None

        # TODO: Implement actual API call when backend is ready
        # API will check quota and return status with tier and usage counts
        return None

    def _record_api_usage(self, _device_id: str, _api_key: str | None) -> bool:
        """
        Record validation usage via API.

        Args:
            _device_id: Device fingerprint (unused until API implemented)
            _api_key: Optional API key for paid tiers (unused until API implemented)

        Returns:
            True if recorded successfully, False otherwise
        """
        if self.offline_mode:
            return False

        # TODO: Implement actual API call when backend is ready
        # API will record usage and return updated count
        return False

    def _is_new_month(self, last_reset_month: str | None) -> bool:
        """Check if we've entered a new month since last reset."""
        if not last_reset_month:
            return True

        current_month = date.today().strftime("%Y-%m")
        return current_month != last_reset_month

    def _create_initial_quota_status(self, device_id: str, api_key: str | None) -> QuotaStatus:
        """Create initial quota status for new device."""
        has_api_key = api_key is not None
        tier: Literal["free", "starter", "pro"] = "starter" if has_api_key else "free"
        quota_limit = self.PAID_TIER_UNLIMITED if has_api_key else self.FREE_TIER_MONTHLY_LIMIT

        return QuotaStatus(
            device_id=device_id,
            validations_this_month=0,
            quota_limit=quota_limit,
            tier=tier,
            last_validation_date=None,
            last_reset_month=date.today().strftime("%Y-%m"),
            quota_exceeded=False,
            has_api_key=has_api_key,
            is_offline=self.offline_mode,
        )

    def check_quota(self) -> QuotaStatus:
        """
        Check if user can perform validation.

        Returns:
            QuotaStatus with current quota state and whether quota is exceeded
        """
        device_id = self.get_device_id()
        api_key = self._get_api_key()

        # Bypass quota in test environment
        if os.environ.get("DEEPSWEEP_SKIP_QUOTA") == "1":
            return QuotaStatus(
                device_id=device_id,
                validations_this_month=0,
                quota_limit=self.PAID_TIER_UNLIMITED,
                tier="free",
                last_validation_date=None,
                last_reset_month=date.today().strftime("%Y-%m"),
                quota_exceeded=False,
                has_api_key=False,
                is_offline=self.offline_mode,
            )

        # If user has API key (paid tier), they get unlimited validations
        # even when offline (we trust the API key as proof of payment)
        if api_key:
            return QuotaStatus(
                device_id=device_id,
                validations_this_month=0,
                quota_limit=self.PAID_TIER_UNLIMITED,
                tier="starter",  # Assume starter tier if API key present
                last_validation_date=None,
                last_reset_month=date.today().strftime("%Y-%m"),
                quota_exceeded=False,
                has_api_key=True,
                is_offline=self.offline_mode,
            )

        # Try API first (if not in offline mode)
        api_status = self._check_api_quota(device_id, api_key)
        if api_status:
            # Save API response locally for offline fallback
            self._save_local_quota(api_status)
            return api_status

        # Fallback to local storage (offline mode or API unavailable)
        local_status = self._load_local_quota()

        if local_status:
            # Check if we need to reset quota (new month)
            if self._is_new_month(local_status.last_reset_month):
                local_status.validations_this_month = 0
                local_status.last_reset_month = date.today().strftime("%Y-%m")
                local_status.quota_exceeded = False
                self._save_local_quota(local_status)

            # Smart offline handling: If user previously had paid tier and is offline, allow
            if local_status.tier in ["starter", "pro"] and self.offline_mode:
                local_status.is_offline = True
                local_status.quota_exceeded = False
                return local_status

            # For free tier users offline, enforce local quota
            local_status.is_offline = True
            local_status.quota_exceeded = (
                local_status.validations_this_month >= local_status.quota_limit
            )
            return local_status

        # No local data - create initial status
        initial_status = self._create_initial_quota_status(device_id, api_key)
        self._save_local_quota(initial_status)
        return initial_status

    def record_validation(self) -> None:
        """
        Record that a validation was performed.

        Updates both API and local storage.
        """
        device_id = self.get_device_id()
        api_key = self._get_api_key()

        # If user has API key (paid tier), no need to track locally
        if api_key:
            # Still try to record to API for analytics
            self._record_api_usage(device_id, api_key)
            return

        # Try to record to API (for analytics)
        self._record_api_usage(device_id, api_key)

        # Always update local storage (for offline fallback)
        local_status = self._load_local_quota()

        if local_status:
            # Increment local counter
            local_status.validations_this_month += 1
            local_status.last_validation_date = datetime.now().isoformat()
            local_status.quota_exceeded = (
                local_status.validations_this_month >= local_status.quota_limit
            )
            self._save_local_quota(local_status)
        else:
            # Create new record
            new_status = self._create_initial_quota_status(device_id, api_key)
            new_status.validations_this_month = 1
            new_status.last_validation_date = datetime.now().isoformat()
            self._save_local_quota(new_status)

    def get_upgrade_message(self, validations_used: int, quota_limit: int) -> str:
        """
        Generate upgrade prompt message.

        Args:
            validations_used: Number of validations used this month
            quota_limit: Monthly quota limit

        Returns:
            Formatted upgrade message (plain English, no emoji)
        """
        return f"""You've used {validations_used} of {quota_limit} free checks this month.

Want unlimited checks? Upgrade to unlock:

  Starter Plan ($9/month)
  - Unlimited security checks
  - Priority support
  - Early access to new features

  Pro Plan ($29/month)
  - Everything in Starter
  - Advanced reporting
  - Team collaboration
  - API access

Upgrade at: https://deepsweep.ai/pricing

Your free quota resets on the 1st of next month."""

    def clear(self) -> None:
        """Clear quota data (for testing/reset)."""
        if self.quota_file.exists():
            with contextlib.suppress(OSError):
                self.quota_file.unlink()


def check_quota(offline_mode: bool = False) -> tuple[bool, str | None]:
    """
    Check if user can perform validation (convenience function).

    Args:
        offline_mode: If True, skip API calls

    Returns:
        Tuple of (can_validate, error_message)
        - can_validate: True if user has quota remaining
        - error_message: Error message if quota exceeded, None otherwise
    """
    enforcer = QuotaEnforcer(offline_mode=offline_mode)
    status = enforcer.check_quota()

    if status.quota_exceeded:
        message = enforcer.get_upgrade_message(
            status.validations_this_month, status.quota_limit
        )
        return False, message

    return True, None


def record_validation(offline_mode: bool = False) -> None:
    """
    Record validation usage (convenience function).

    Args:
        offline_mode: If True, skip API calls
    """
    enforcer = QuotaEnforcer(offline_mode=offline_mode)
    enforcer.record_validation()
