"""License key validation for Meta Ads MCP Server.

Implements offline HMAC-SHA256 signed license keys with tier-based tool gating.
Free tier provides ~10 read-only tools. Paid tiers unlock the full suite.
"""

from __future__ import annotations

import base64
import hashlib
import hmac
import json
from dataclasses import dataclass
from datetime import date, datetime
from typing import Optional

from .logging import get_logger

logger = get_logger(__name__)

# The HMAC signing secret is embedded in the package. This is NOT a security
# boundary against reverse engineering — Python source is readable. The purpose
# is to prevent casual key forgery and provide a clean licensing UX. Real
# protection comes from the value of ongoing updates and support.
_SIGNING_SECRET = "mads-2026-f7a3b9c1e5d2"

# Tools available without a license key
FREE_TIER_TOOLS = frozenset({
    "meta.graph.request",
    "meta.insights.get",
    "meta.business.list",
    "meta.business.get",
    "meta.business.list_ad_accounts",
    "meta.search_campaigns",
    "meta.campaign_performance_history",
    "meta.spend_trends",
    "meta.get_audience_insights",
    "meta.get_campaign_tags",
})


@dataclass
class LicenseInfo:
    """Decoded license key information."""
    tier: str  # "pro" or "enterprise"
    email: str
    expires: date
    valid: bool
    error: Optional[str] = None


def _sign(payload_b64: bytes, secret: str) -> str:
    """Create HMAC-SHA256 signature for a base64-encoded payload."""
    return hmac.new(
        secret.encode("utf-8"),
        payload_b64,
        hashlib.sha256,
    ).hexdigest()


def generate_license_key(
    tier: str,
    email: str,
    expires: str,
    signing_secret: Optional[str] = None,
) -> str:
    """Generate a signed license key.

    Args:
        tier: License tier ("pro" or "enterprise")
        email: Licensee email
        expires: Expiry date as YYYY-MM-DD string
        signing_secret: HMAC secret (defaults to embedded secret)

    Returns:
        Base64-encoded payload + "." + hex signature
    """
    secret = signing_secret or _SIGNING_SECRET
    payload = json.dumps({
        "tier": tier,
        "email": email,
        "exp": expires,
    }, separators=(",", ":"), sort_keys=True)
    payload_b64 = base64.urlsafe_b64encode(payload.encode("utf-8"))
    signature = _sign(payload_b64, secret)
    return payload_b64.decode("utf-8") + "." + signature


def validate_license_key(
    key: str,
    signing_secret: Optional[str] = None,
) -> LicenseInfo:
    """Validate a license key and return its info.

    Args:
        key: The license key string (payload.signature)
        signing_secret: HMAC secret (defaults to embedded secret)

    Returns:
        LicenseInfo with valid=True on success, valid=False with error on failure
    """
    secret = signing_secret or _SIGNING_SECRET

    try:
        parts = key.strip().split(".")
        if len(parts) != 2:
            return LicenseInfo(tier="free", email="", expires=date.min, valid=False,
                               error="Invalid key format")

        payload_b64, signature = parts[0].encode("utf-8"), parts[1]

        # Verify HMAC signature
        expected_sig = _sign(payload_b64, secret)
        if not hmac.compare_digest(signature, expected_sig):
            return LicenseInfo(tier="free", email="", expires=date.min, valid=False,
                               error="Invalid license key")

        # Decode payload
        payload = json.loads(base64.urlsafe_b64decode(payload_b64))
        tier = payload.get("tier", "free")
        email = payload.get("email", "")
        exp_str = payload.get("exp", "")

        # Check expiry
        expires = datetime.strptime(exp_str, "%Y-%m-%d").date()
        if expires < date.today():
            return LicenseInfo(tier=tier, email=email, expires=expires, valid=False,
                               error="License key expired")

        if tier not in ("pro", "enterprise"):
            return LicenseInfo(tier=tier, email=email, expires=expires, valid=False,
                               error=f"Unknown tier: {tier}")

        return LicenseInfo(tier=tier, email=email, expires=expires, valid=True)

    except Exception as e:
        return LicenseInfo(tier="free", email="", expires=date.min, valid=False,
                           error=f"License validation error: {e}")


def get_license_tier() -> str:
    """Get the current license tier from settings.

    Returns "free" if no key or invalid key, otherwise "pro" or "enterprise".
    """
    try:
        from .config import get_settings
        settings = get_settings()
    except RuntimeError:
        return "free"

    if not settings.license_key:
        return "free"

    info = validate_license_key(settings.license_key)
    if not info.valid:
        logger.warning("license_invalid error=%s", info.error)
        return "free"

    return info.tier


def is_tool_allowed(tool_name: str, tier: str) -> bool:
    """Check if a tool is accessible for the given license tier.

    Args:
        tool_name: The tool name (e.g., "meta.insights.get")
        tier: License tier ("free", "pro", or "enterprise")

    Returns:
        True if the tool is allowed for this tier
    """
    if tier in ("pro", "enterprise"):
        return True
    return tool_name in FREE_TIER_TOOLS
