"""License key validation for stablestack — server-side API validation."""

from __future__ import annotations

import json
import logging
import os
import platform
import sys
import urllib.error
import urllib.request
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from typing import Optional

logger = logging.getLogger(__name__)

LICENSE_API_URL = "https://hy09uhnly0.execute-api.us-east-1.amazonaws.com/v1/licenses/validate"

# Re-validate cached license every 7 days
_REVALIDATION_DAYS = 7
# Allow offline use for 30 days after last successful validation
_OFFLINE_GRACE_DAYS = 30


@dataclass
class LicenseInfo:
    """Information about a valid license."""
    customer_name: str
    customer_email: str
    license_type: str  # "trial", "individual", "team", "enterprise"
    expires_at: Optional[datetime]
    max_projects: int  # 0 = unlimited
    download_url: str = ""  # Pre-signed URL for checker bundle


class LicenseError(Exception):
    """Raised when license validation fails."""
    pass


def _get_license_dir() -> Path:
    """Return ~/.stablestack/, creating it if needed."""
    d = Path.home() / ".stablestack"
    d.mkdir(parents=True, exist_ok=True)
    return d


def _get_license_cache_path() -> Path:
    return _get_license_dir() / "license.json"


def validate_license_server(key: str) -> dict:
    """
    Validate a license key against the server API.

    POSTs to the license API with the key, platform, arch, and version.

    Returns:
        Server response as a dict containing license metadata and a
        presigned download_url for the compiled checker bundle.

    Raises:
        LicenseError: If the server rejects the key or is unreachable.
    """
    try:
        from stablestack import __version__
    except ImportError:
        __version__ = "0.0.0"

    vi = sys.version_info
    python_tag = f"cp{vi.major}{vi.minor}"

    payload = json.dumps({
        "key": key,
        "platform": sys.platform,
        "arch": platform.machine(),
        "version": __version__,
        "python_version": python_tag,
    }).encode("utf-8")

    req = urllib.request.Request(
        LICENSE_API_URL,
        data=payload,
        headers={
            "Content-Type": "application/json",
            "User-Agent": f"stablestack/{__version__}",
        },
        method="POST",
    )

    try:
        with urllib.request.urlopen(req, timeout=15) as resp:
            data = json.loads(resp.read().decode("utf-8"))
    except urllib.error.HTTPError as e:
        body = ""
        try:
            body = e.read().decode("utf-8", errors="replace")
            err = json.loads(body)
            msg = err.get("error", body)
        except Exception:
            msg = body or str(e)
        raise LicenseError(f"License validation failed: {msg}") from e
    except urllib.error.URLError as e:
        raise LicenseError(
            f"Could not reach license server: {e.reason}"
        ) from e

    if not data.get("valid"):
        raise LicenseError(data.get("error", "License key is not valid"))

    return data


def save_license_cache(key: str, server_response: dict) -> None:
    """Save license info to ~/.stablestack/license.json."""
    cache = {
        "key": key,
        "customer_name": server_response.get("customer_name", ""),
        "customer_email": server_response.get("customer_email", ""),
        "license_type": server_response.get("license_type", "individual"),
        "expires_at": server_response.get("expires_at", ""),
        "max_projects": server_response.get("max_projects", 0),
        "validated_at": datetime.now().isoformat(),
    }
    _get_license_cache_path().write_text(
        json.dumps(cache, indent=2), encoding="utf-8"
    )


def load_license_cache() -> Optional[dict]:
    """Load cached license info from ~/.stablestack/license.json."""
    path = _get_license_cache_path()
    if not path.exists():
        return None
    try:
        return json.loads(path.read_text(encoding="utf-8"))
    except (json.JSONDecodeError, OSError):
        return None


def _parse_license_info(cache: dict) -> LicenseInfo:
    """Build a LicenseInfo from cached data."""
    expires_str = cache.get("expires_at", "")
    try:
        expires_at = datetime.fromisoformat(expires_str) if expires_str else None
    except (ValueError, TypeError):
        expires_at = None

    return LicenseInfo(
        customer_name=cache.get("customer_name", ""),
        customer_email=cache.get("customer_email", ""),
        license_type=cache.get("license_type", "individual"),
        expires_at=expires_at,
        max_projects=cache.get("max_projects", 0),
    )


def get_license_key() -> Optional[str]:
    """
    Get the license key from environment or cache.

    Checks in order:
    1. STABLESTACK_LICENSE environment variable
    2. Cached key in ~/.stablestack/license.json
    """
    # Check environment variable
    env_key = os.environ.get("STABLESTACK_LICENSE")
    if env_key:
        return env_key.strip()

    # Check cached license
    cache = load_license_cache()
    if cache and cache.get("key"):
        return cache["key"]

    return None


def check_license() -> tuple[bool, Optional[LicenseInfo], Optional[str]]:
    """
    Check if the current installation has a valid license.

    Uses the local cache. Re-validates against the server every 7 days.
    Allows a 30-day offline grace period after the last successful validation.

    Returns:
        Tuple of (is_valid, license_info, error_message)
    """
    cache = load_license_cache()

    if not cache or not cache.get("key"):
        # No cached license — check legacy env/file
        key = get_license_key()
        if not key:
            return False, None, "No license key found"
        # Have a key but no server-validated cache yet.
        # Try to validate now.
        try:
            data = validate_license_server(key)
            save_license_cache(key, data)
            return True, _parse_license_info(data), None
        except LicenseError as e:
            return False, None, str(e)

    # We have a cached license.
    info = _parse_license_info(cache)

    # Check if license has expired (None = never expires)
    if info.expires_at is not None and info.expires_at < datetime.now():
        return False, None, f"License expired on {info.expires_at.date()}"

    # Check if we need to re-validate with the server
    validated_at_str = cache.get("validated_at", "")
    try:
        validated_at = datetime.fromisoformat(validated_at_str)
    except (ValueError, TypeError):
        validated_at = datetime.min

    days_since = (datetime.now() - validated_at).days

    if days_since >= _REVALIDATION_DAYS:
        # Try to re-validate
        try:
            data = validate_license_server(cache["key"])
            save_license_cache(cache["key"], data)
            return True, _parse_license_info(data), None
        except LicenseError:
            # Server unreachable — allow offline grace period
            if days_since <= _OFFLINE_GRACE_DAYS:
                return True, info, None
            return False, None, (
                "License re-validation failed and offline grace period "
                f"({_OFFLINE_GRACE_DAYS} days) has expired"
            )

    return True, info, None


# License check result caching (per-process)
_cached_license: Optional[tuple[bool, Optional[LicenseInfo], Optional[str]]] = None


def get_cached_license() -> tuple[bool, Optional[LicenseInfo], Optional[str]]:
    """Get cached license check result, performing check if needed."""
    global _cached_license
    if _cached_license is None:
        _cached_license = check_license()
    return _cached_license


def invalidate_license_cache() -> None:
    """Reset the in-process license cache (e.g. after activation)."""
    global _cached_license
    _cached_license = None
