"""
PULSE License Manager — generate, validate, store, and check subscription plans.

Uses PulseCrypto HMAC for key signing. Validates against Stripe API.
License stored at ~/.pulse/license.json with 24h cache.
"""
import json
import os
import time
from pathlib import Path
from typing import Optional

from pulse.core.paths import get_pulse_home, _is_dev_mode

# --- Plan definitions ---

CORE_DAEMONS = [
    "bridge_daemon", "neural_daemon", "compliance_daemon",
    "dispatcher_daemon", "api_daemon", "worker_supervisor",
]

PLAN_LIMITS = {
    "solo": {"max_workers": 5, "daemons": CORE_DAEMONS},
    "pro":  {"max_workers": None, "daemons": None},  # None = unlimited / all
    "trial_expired": {"max_workers": 0, "daemons": []},
    "none": {"max_workers": 0, "daemons": []},
}

# Stripe price ID → plan name (set from Stripe dashboard)
PRICE_TO_PLAN = {
    "price_1T6ccDE0Lm8KpA1GtuvBFMxB": "solo",
    "price_1T6cdAE0Lm8KpA1G6yxGqcca": "pro",
}

CACHE_TTL = 86400       # 24h — trust cache
CACHE_WARN = 172800     # 48h — warn but still trust


class LicenseManager:
    """Handles license key generation, validation, and plan enforcement."""

    def __init__(self):
        self._license_file = get_pulse_home() / "license.json"

    # --- Key generation ---

    def generate_key(self, plan: str, stripe_customer_id: str) -> str:
        """Generate HMAC-signed license key: PULSE-{PLAN}-{cus_id}-{signature}."""
        from pulse.core.pulse_crypto import PulseCrypto
        crypto = PulseCrypto()
        payload = f"{plan}:{stripe_customer_id}"
        signature = crypto.sign_string(payload, "license")
        return f"PULSE-{plan.upper()}-{stripe_customer_id}-{signature[:24]}"

    # --- Key validation ---

    def validate_key(self, key: str) -> Optional[dict]:
        """Validate license key against PULSE license server.

        Returns validation dict or None if invalid.
        """
        parsed = self._parse_key(key)
        if not parsed:
            return None

        plan, customer_id, _ = parsed

        try:
            import requests
            resp = requests.post(
                "https://pulseos.dev/api/license/validate",
                json={"key": key},
                timeout=10,
            )
            if resp.status_code != 200:
                return None
            data = resp.json()
            if not data.get("valid"):
                return None
            return {
                "plan": data.get("plan", plan),
                "status": data.get("status", "unknown"),
                "customer_id": customer_id,
                "trial_end": data.get("trial_end"),
                "cached_at": time.time(),
            }
        except Exception:
            return None

    # --- License storage ---

    def save_license(self, key: str, validation: dict):
        """Write license + validation to ~/.pulse/license.json."""
        self._license_file.parent.mkdir(parents=True, exist_ok=True)
        data = {**validation, "key": key}
        self._license_file.write_text(json.dumps(data, indent=2), encoding="utf-8")

    def load_license(self) -> Optional[dict]:
        """Load cached license. Re-validates if cache is stale."""
        if not self._license_file.exists():
            return None
        try:
            data = json.loads(self._license_file.read_text(encoding="utf-8"))
        except (json.JSONDecodeError, OSError):
            return None

        cached_at = data.get("cached_at", 0)
        age = time.time() - cached_at

        if age < CACHE_TTL:
            return data

        if age < CACHE_WARN:
            # Stale but acceptable — warn
            data["_warning"] = "License cache stale. Will re-validate on next boot."
            return data

        # Over 48h — try re-validation, fall back to cache if offline
        key = data.get("key")
        if key:
            refreshed = self.validate_key(key)
            if refreshed:
                self.save_license(key, refreshed)
                return refreshed

        # Offline fallback — degrade gracefully
        data["_warning"] = "Offline >48h. Trusting cached license."
        return data

    # --- Plan queries ---

    def get_plan(self) -> str:
        """Return current plan: 'solo' | 'pro' | 'trial_expired' | 'none'."""
        if _is_dev_mode():
            return "pro"

        lic = self.load_license()
        if not lic:
            return "none"

        status = lic.get("status", "")
        if status in ("active", "trialing"):
            return lic.get("plan", "solo")
        if status == "canceled":
            return "trial_expired"
        return "trial_expired"

    def get_limits(self) -> dict:
        """Return plan limits: {max_workers, daemons}."""
        plan = self.get_plan()
        return PLAN_LIMITS.get(plan, PLAN_LIMITS["none"]).copy()

    # --- Internals ---

    @staticmethod
    def _parse_key(key: str) -> Optional[tuple]:
        """Parse PULSE-PLAN-cus_xxx-signature → (plan, customer_id, sig_fragment)."""
        parts = key.split("-", 3)
        if len(parts) != 4 or parts[0] != "PULSE":
            return None
        plan = parts[1].lower()
        if plan not in ("solo", "pro"):
            return None
        # parts[2] could be "cus_xxx" but the customer_id has an underscore
        # Key format: PULSE-SOLO-cus_abc123-sig24chars
        # We need to re-split: everything between 2nd and last dash
        # Actually the format is: PULSE-{PLAN}-{cus_id}-{sig}
        # But cus_id contains underscores, not dashes. So split on "-" with maxsplit=3 works.
        customer_id = parts[2]
        sig_fragment = parts[3]
        if not customer_id.startswith("cus_"):
            return None
        return plan, customer_id, sig_fragment

    @staticmethod
    def _check_stripe(customer_id: str) -> Optional[dict]:
        """Query Stripe API for active subscription."""
        secret = os.environ.get("STRIPE_SECRET_KEY")
        if not secret:
            return None
        try:
            import requests
            resp = requests.get(
                "https://api.stripe.com/v1/subscriptions",
                params={"customer": customer_id, "limit": 1},
                auth=(secret, ""),
                timeout=10,
            )
            if resp.status_code != 200:
                return None
            data = resp.json()
            subs = data.get("data", [])
            if not subs:
                return None
            sub = subs[0]
            return {
                "status": sub.get("status"),
                "trial_end": sub.get("trial_end"),
            }
        except Exception:
            return None
