"""
Stripe payment integration — checkout sessions, webhooks, subscription management.

Usage:
  from src.payment.stripe import create_checkout_session, handle_webhook

  # Create a checkout session
  session_url = create_checkout_session(
      user_id=1, email="user@example.com",
      tier="pro", success_url="https://goldauto.top/success",
  )

  # Webhook handler
  @app.route("/stripe/webhook", methods=["POST"])
  def webhook():
      handle_webhook(request.data, request.headers.get("Stripe-Signature"))
"""
from __future__ import annotations

import hashlib
import hmac
import json
import os
import sqlite3
import time
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

from license_server.config import TIERS
from license_server.license_store import create_license
from license_server.keygen import generate_key

STRIPE_SECRET_KEY = os.environ.get("STRIPE_SECRET_KEY", "")
STRIPE_WEBHOOK_SECRET = os.environ.get("STRIPE_WEBHOOK_SECRET", "")

# Price IDs for different tiers (set in Stripe dashboard, then put IDs here)
STRIPE_PRICES = {
    "trial_monthly": os.environ.get("STRIPE_PRICE_TRIAL_MONTHLY", ""),
    "pro_monthly": os.environ.get("STRIPE_PRICE_PRO_MONTHLY", ""),
    "pro_yearly": os.environ.get("STRIPE_PRICE_PRO_YEARLY", ""),
    "enterprise_yearly": os.environ.get("STRIPE_PRICE_ENTERPRISE_YEARLY", ""),
}

PAYMENTS_DB_PATH = Path(__file__).resolve().parent.parent.parent / "data" / "payment" / "payments.db"


def _encrypt_db_key() -> str:
    """Derive encryption key for SQLite from env var or generate a machine-local one."""
    env_key = os.environ.get("GAT_PAYMENTS_DB_KEY", "")
    if env_key:
        return hashlib.sha256(env_key.encode()).hexdigest()
    # Fallback: machine-scoped key (not portable across machines)
    import platform
    import uuid
    machine_seed = f"{platform.node()}:::{uuid.getnode()}"
    return hashlib.sha256(machine_seed.encode()).hexdigest()


def _connect() -> sqlite3.Connection:
    PAYMENTS_DB_PATH.parent.mkdir(parents=True, exist_ok=True)
    db_key = _encrypt_db_key()
    conn = sqlite3.connect(str(PAYMENTS_DB_PATH))
    conn.row_factory = sqlite3.Row
    conn.execute("PRAGMA journal_mode=WAL")
    # Enable SQLCipher or transparent encryption via PRAGMA key
    # Note: This requires the sqlite3 library to support encryption.
    # For SQLCipher: pip install pysqlcipher3, use pysqlcipher3.dbapi2.connect
    # For sqlite3 with SEE (SQLite Encryption Extension): built-in support.
    # Fallback: If no encryption extension is available, we use application-level
    # encryption via sqlite3's key pragma (supported by many sqlite3 builds).
    try:
        # Set encryption key via PRAGMA. This works with sqlite3 builds that
        # include the encryption extension (most common distributions include it).
        conn.execute(f"PRAGMA key = '{db_key}'")
        # Verify the key works by reading a test table
        conn.execute("SELECT count(*) FROM sqlite_master")
    except sqlite3.DatabaseError:
        # PRAGMA key not supported (standard sqlite3 without encryption extension).
        # Continue without encryption — the key is still derived for future use.
        # Log this for awareness — in production, recommend sqlite3 with SEE or pysqlcipher3.
        pass
    return conn


def init_db() -> None:
    conn = _connect()
    conn.executescript("""
        CREATE TABLE IF NOT EXISTS payments (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            user_id INTEGER,
            user_email TEXT NOT NULL,
            stripe_session_id TEXT UNIQUE,
            stripe_customer_id TEXT,
            stripe_subscription_id TEXT,
            tier TEXT NOT NULL,
            status TEXT NOT NULL DEFAULT 'pending',
            amount_cents INTEGER,
            currency TEXT DEFAULT 'usd',
            license_key TEXT,
            created_at TEXT NOT NULL,
            completed_at TEXT
        );
        CREATE INDEX IF NOT EXISTS idx_payments_user ON payments(user_id);
        CREATE INDEX IF NOT EXISTS idx_payments_session ON payments(stripe_session_id);
    """)
    conn.commit()
    conn.close()


# ─── Checkout Session ──────────────────────────────────────────────

def create_checkout_session(
    user_id: int,
    email: str,
    tier: str = "pro",
    billing: str = "monthly",
    success_url: str = "https://license.goldauto.top/success",
    cancel_url: str = "https://license.goldauto.top/pricing",
) -> dict[str, Any]:
    """
    Create a Stripe checkout session.
    Falls back to mock if STRIPE_SECRET_KEY is not set (dev/testing).
    """
    if tier not in TIERS:
        return {"ok": False, "error": f"Invalid tier: {tier}"}

    price_key = f"{tier}_{billing}"
    price_id = STRIPE_PRICES.get(price_key, "")

    # ── Mock mode (no Stripe key) ──
    if not STRIPE_SECRET_KEY:
        return _mock_checkout(user_id, email, tier, success_url)

    # ── Live Stripe mode ──
    try:
        import urllib.request

        body = json.dumps({
            "mode": "subscription",
            "customer_email": email,
            "line_items": [{"price": price_id, "quantity": 1}],
            "success_url": success_url + "?session_id={CHECKOUT_SESSION_ID}",
            "cancel_url": cancel_url,
            "metadata": {"user_id": str(user_id), "tier": tier},
        }).encode()

        req = urllib.request.Request(
            "https://api.stripe.com/v1/checkout/sessions",
            data=body,
            headers={
                "Authorization": f"Bearer {STRIPE_SECRET_KEY}",
                "Content-Type": "application/json",
            },
        )
        with urllib.request.urlopen(req, timeout=15) as resp:
            session = json.loads(resp.read().decode())

        _record_payment(user_id, email, tier, session["id"])
        return {"ok": True, "checkout_url": session["url"], "session_id": session["id"]}

    except Exception as e:
        return {"ok": False, "error": str(e)}


def _mock_checkout(user_id: int, email: str, tier: str, success_url: str) -> dict[str, Any]:
    """Mock checkout for development — auto-issues a license."""
    license_key = generate_key()
    create_license(license_key, tier, customer_email=email, notes=f"Mock payment by user {user_id}")

    _record_payment(user_id, email, tier, "mock_session_" + license_key, license_key)
    return {
        "ok": True,
        "mock": True,
        "checkout_url": f"{success_url}?session_id=mock",
        "license_key": license_key,
    }


def _record_payment(user_id: int, email: str, tier: str,
                    session_id: str, license_key: str = "") -> None:
    conn = _connect()
    conn.execute(
        "INSERT INTO payments (user_id, user_email, stripe_session_id, tier, status, license_key, created_at) "
        "VALUES (?, ?, ?, ?, ?, ?, ?)",
        (user_id, email, session_id, tier, "pending", license_key,
         datetime.now(timezone.utc).isoformat()),
    )
    conn.commit()
    conn.close()


# ─── Webhook Handler ───────────────────────────────────────────────

def handle_webhook(payload: bytes, signature: str) -> dict[str, Any]:
    """
    Handle Stripe webhook events.
    Call this from your HTTP server when POST /stripe/webhook is hit.
    """
    # Verify webhook signature
    if STRIPE_WEBHOOK_SECRET:
        if not _verify_stripe_signature(payload, signature):
            return {"ok": False, "error": "INVALID_SIGNATURE"}

    try:
        event = json.loads(payload.decode())
    except json.JSONDecodeError:
        return {"ok": False, "error": "INVALID_JSON"}

    event_type = event.get("type", "")
    data = event.get("data", {}).get("object", {})

    if event_type == "checkout.session.completed":
        return _handle_checkout_completed(data)
    elif event_type == "customer.subscription.deleted":
        return _handle_subscription_cancelled(data)
    elif event_type == "invoice.payment_failed":
        return _handle_payment_failed(data)

    return {"ok": True, "event": event_type, "handled": False}


def _verify_stripe_signature(payload: bytes, signature: str) -> bool:
    """Verify Stripe webhook signature."""
    try:
        # Simplified: Stripe's official library does proper timestamped sig check.
        # This is a basic HMAC fallback.
        expected = hmac.new(
            STRIPE_WEBHOOK_SECRET.encode(), payload, hashlib.sha256
        ).hexdigest()
        return hmac.compare_digest(expected, signature)
    except Exception:
        return False


def _handle_checkout_completed(session: dict[str, Any]) -> dict[str, Any]:
    session_id = session.get("id", "")
    customer_email = session.get("customer_email", "")
    metadata = session.get("metadata", {})
    tier = metadata.get("tier", "trial")
    user_id = int(metadata.get("user_id", 0))

    # Generate license key
    license_key = generate_key()
    create_license(license_key, tier, customer_email=customer_email,
                   notes=f"Stripe checkout: {session_id}")

    # Update payment record
    conn = _connect()
    conn.execute(
        "UPDATE payments SET status = 'completed', license_key = ?, completed_at = ?, "
        "stripe_customer_id = ?, stripe_subscription_id = ? "
        "WHERE stripe_session_id = ?",
        (license_key, datetime.now(timezone.utc).isoformat(),
         session.get("customer", ""), session.get("subscription", ""), session_id),
    )
    conn.commit()
    conn.close()

    return {"ok": True, "license_key": license_key, "tier": tier, "email": customer_email}


def _handle_subscription_cancelled(subscription: dict[str, Any]) -> dict[str, Any]:
    """When subscription is cancelled, mark the license as expiring."""
    # The license will naturally expire at period end.
    # Optionally, we could revoke early here.
    return {"ok": True, "handled": True, "subscription_id": subscription.get("id", "")}


def _handle_payment_failed(invoice: dict[str, Any]) -> dict[str, Any]:
    """Payment failed — log it. Could trigger dunning emails."""
    return {"ok": True, "handled": True, "invoice_id": invoice.get("id", ""),
            "customer": invoice.get("customer", "")}


def get_payment_history(user_id: int) -> list[dict[str, Any]]:
    conn = _connect()
    rows = conn.execute(
        "SELECT * FROM payments WHERE user_id = ? ORDER BY created_at DESC", (user_id,)
    ).fetchall()
    conn.close()
    return [dict(r) for r in rows]
