#!/usr/bin/env python3
"""
MEDUSA Licensing Module

Manages license tiers and feature gating for MEDUSA Security Scanner.

License Tiers:
- FREE: Basic scanner patterns, community rules, SARIF output
- PROFESSIONAL: + Runtime filters, API access, priority support
- ENTERPRISE: + Custom rules, SSO, dedicated support, SLA

Runtime filters are PAID-ONLY features (Professional+)
"""

import os
import json
import hashlib
import base64
from pathlib import Path
from dataclasses import dataclass, field
from typing import Optional, List, Dict, Any
from enum import Enum
from datetime import datetime


class LicenseTier(Enum):
    """License tier levels"""
    FREE = "free"
    PROFESSIONAL = "professional"
    ENTERPRISE = "enterprise"


@dataclass
class LicenseInfo:
    """License information"""
    tier: LicenseTier
    email: Optional[str] = None
    organization: Optional[str] = None
    expires_at: Optional[datetime] = None
    features: List[str] = field(default_factory=list)
    max_repos: int = 1  # Free tier: 1 repo
    api_access: bool = False
    runtime_filters: bool = False  # CRITICAL: Paid-only feature
    custom_rules: bool = False

    @property
    def is_valid(self) -> bool:
        """Check if license is currently valid"""
        if self.tier == LicenseTier.FREE:
            return True
        if self.expires_at is None:
            return False
        return datetime.now() < self.expires_at

    @property
    def is_paid(self) -> bool:
        """Check if this is a paid license"""
        return self.tier in (LicenseTier.PROFESSIONAL, LicenseTier.ENTERPRISE)


# Feature definitions by tier
TIER_FEATURES = {
    LicenseTier.FREE: {
        'basic_scanners': True,           # 74 built-in scanners
        'scanner_patterns': True,          # Pattern-based detection
        'yaml_rules_scanner': True,        # Scanner YAML rules only
        'sarif_output': True,              # SARIF for GitHub integration
        'json_output': True,
        'html_output': True,
        'cli_access': True,
        'github_action': True,
        'max_repos': 1,
        # PAID FEATURES - Disabled for free
        'runtime_filters': False,          # Runtime YAML rules
        'api_access': False,               # REST API
        'webhooks': False,
        'custom_rules': False,
        'priority_support': False,
        'sso': False,
        'audit_logs': False,
    },
    LicenseTier.PROFESSIONAL: {
        'basic_scanners': True,
        'scanner_patterns': True,
        'yaml_rules_scanner': True,
        'sarif_output': True,
        'json_output': True,
        'html_output': True,
        'cli_access': True,
        'github_action': True,
        'max_repos': 10,
        # PAID FEATURES - Enabled
        'runtime_filters': True,           # Runtime YAML rules
        'api_access': True,                # REST API
        'webhooks': True,
        'custom_rules': False,             # Enterprise only
        'priority_support': True,
        'sso': False,                      # Enterprise only
        'audit_logs': False,               # Enterprise only
    },
    LicenseTier.ENTERPRISE: {
        'basic_scanners': True,
        'scanner_patterns': True,
        'yaml_rules_scanner': True,
        'sarif_output': True,
        'json_output': True,
        'html_output': True,
        'cli_access': True,
        'github_action': True,
        'max_repos': -1,  # Unlimited
        # ALL PAID FEATURES
        'runtime_filters': True,
        'api_access': True,
        'webhooks': True,
        'custom_rules': True,
        'priority_support': True,
        'sso': True,
        'audit_logs': True,
    },
}


class LicenseManager:
    """
    Manages MEDUSA licensing and feature access.

    License keys are stored in:
    1. Environment variable: MEDUSA_LICENSE_KEY
    2. Config file: ~/.medusa/license.json
    3. Project file: .medusa/license.json

    Usage:
        manager = LicenseManager()
        license_info = manager.get_license()

        if manager.has_feature('runtime_filters'):
            # Load runtime rules
            pass
    """

    LICENSE_ENV_VAR = "MEDUSA_LICENSE_KEY"
    GLOBAL_LICENSE_PATH = Path.home() / ".medusa" / "license.json"
    PROJECT_LICENSE_PATH = Path(".medusa") / "license.json"

    def __init__(self):
        self._license: Optional[LicenseInfo] = None
        self._cached = False

    def get_license(self) -> LicenseInfo:
        """
        Get current license information.

        Checks in order:
        1. Environment variable
        2. Global config file
        3. Project config file
        4. Returns FREE tier if no license found
        """
        if self._cached and self._license:
            return self._license

        # Try environment variable first
        license_key = os.environ.get(self.LICENSE_ENV_VAR)
        if license_key:
            license_info = self._validate_license_key(license_key)
            if license_info:
                self._license = license_info
                self._cached = True
                return self._license

        # Try global config
        if self.GLOBAL_LICENSE_PATH.exists():
            license_info = self._load_license_file(self.GLOBAL_LICENSE_PATH)
            if license_info and license_info.is_valid:
                self._license = license_info
                self._cached = True
                return self._license

        # Try project config
        if self.PROJECT_LICENSE_PATH.exists():
            license_info = self._load_license_file(self.PROJECT_LICENSE_PATH)
            if license_info and license_info.is_valid:
                self._license = license_info
                self._cached = True
                return self._license

        # Default to FREE tier
        self._license = LicenseInfo(tier=LicenseTier.FREE)
        self._cached = True
        return self._license

    def _validate_license_key(self, key: str) -> Optional[LicenseInfo]:
        """Validate a license key and return license info"""
        try:
            # License key format: base64(json{tier, email, org, exp, sig})
            decoded = base64.b64decode(key)
            data = json.loads(decoded)

            # Verify signature (simplified - production would use proper crypto)
            expected_sig = self._compute_signature(data)
            if data.get('sig') != expected_sig:
                return None

            tier = LicenseTier(data.get('tier', 'free'))
            expires_at = None
            if data.get('exp'):
                expires_at = datetime.fromisoformat(data['exp'])

            features = TIER_FEATURES.get(tier, TIER_FEATURES[LicenseTier.FREE])

            return LicenseInfo(
                tier=tier,
                email=data.get('email'),
                organization=data.get('org'),
                expires_at=expires_at,
                features=list(features.keys()),
                max_repos=features.get('max_repos', 1),
                api_access=features.get('api_access', False),
                runtime_filters=features.get('runtime_filters', False),
                custom_rules=features.get('custom_rules', False),
            )
        except Exception:
            return None

    def _load_license_file(self, path: Path) -> Optional[LicenseInfo]:
        """Load license from JSON file"""
        try:
            with open(path) as f:
                data = json.load(f)

            if 'key' in data:
                return self._validate_license_key(data['key'])

            # Direct license info (for testing/development)
            tier = LicenseTier(data.get('tier', 'free'))
            expires_at = None
            if data.get('expires_at'):
                expires_at = datetime.fromisoformat(data['expires_at'])

            features = TIER_FEATURES.get(tier, TIER_FEATURES[LicenseTier.FREE])

            return LicenseInfo(
                tier=tier,
                email=data.get('email'),
                organization=data.get('organization'),
                expires_at=expires_at,
                features=list(features.keys()),
                max_repos=features.get('max_repos', 1),
                api_access=features.get('api_access', False),
                runtime_filters=features.get('runtime_filters', False),
                custom_rules=features.get('custom_rules', False),
            )
        except Exception:
            return None

    def _compute_signature(self, data: Dict[str, Any]) -> str:
        """Compute license signature (simplified)"""
        # In production, use proper cryptographic signing
        content = f"{data.get('tier')}:{data.get('email')}:{data.get('exp')}"
        return hashlib.sha256(content.encode()).hexdigest()[:16]

    def has_feature(self, feature: str) -> bool:
        """Check if current license has a specific feature"""
        license_info = self.get_license()
        tier_features = TIER_FEATURES.get(license_info.tier, TIER_FEATURES[LicenseTier.FREE])
        return tier_features.get(feature, False)

    def can_use_runtime_filters(self) -> bool:
        """
        Check if runtime filters are available.

        Runtime filters are PAID-ONLY (Professional+).
        This is the primary feature gate for v2026.
        """
        return self.has_feature('runtime_filters')

    def get_tier_name(self) -> str:
        """Get human-readable tier name"""
        license_info = self.get_license()
        return license_info.tier.value.title()

    def get_feature_list(self) -> Dict[str, bool]:
        """Get all features and their availability"""
        license_info = self.get_license()
        return TIER_FEATURES.get(license_info.tier, TIER_FEATURES[LicenseTier.FREE]).copy()

    def print_license_info(self):
        """Print license information to console"""
        license_info = self.get_license()
        print(f"\n{'='*50}")
        print(f"MEDUSA License: {license_info.tier.value.upper()}")
        print(f"{'='*50}")

        if license_info.email:
            print(f"Email: {license_info.email}")
        if license_info.organization:
            print(f"Organization: {license_info.organization}")
        if license_info.expires_at:
            print(f"Expires: {license_info.expires_at.strftime('%Y-%m-%d')}")

        print(f"\nFeatures:")
        features = self.get_feature_list()
        for feature, enabled in sorted(features.items()):
            status = "✅" if enabled else "❌"
            print(f"  {status} {feature}")

        if not license_info.is_paid:
            print(f"\n💡 Upgrade to Professional for runtime filters!")
            print(f"   Visit: https://pantheonsecurity.io/pricing")


# Singleton instance
_manager_instance: Optional[LicenseManager] = None


def get_license_manager() -> LicenseManager:
    """Get the singleton LicenseManager instance"""
    global _manager_instance
    if _manager_instance is None:
        _manager_instance = LicenseManager()
    return _manager_instance


def has_feature(feature: str) -> bool:
    """Convenience function to check feature availability"""
    return get_license_manager().has_feature(feature)


def can_use_runtime_filters() -> bool:
    """Convenience function to check runtime filter access"""
    return get_license_manager().can_use_runtime_filters()


def get_license() -> LicenseInfo:
    """Convenience function to get license info"""
    return get_license_manager().get_license()
