#!/usr/bin/env python3
"""
ModuleDesk — Stripe product and price setup script.

Creates (or updates) the four Stripe products and prices required by
billing_service.py. Idempotent: safe to re-run; uses price lookup_keys and
product metadata searches to find existing resources and update them rather
than creating duplicates.

Usage:
    venv/bin/python scripts/setup_stripe_products.py           # test mode
    venv/bin/python scripts/setup_stripe_products.py --live    # live mode

Safety guard: refuses to run against a live key (sk_live_...) unless --live
is explicitly passed on the command line.

At the end the script prints the four STRIPE_PRICE_* lines ready to paste
into .env.

Requirements: stripe>=7.0.0 (already in requirements.txt)
If missing: venv/bin/pip install stripe
"""

from __future__ import annotations

import argparse
import os
import sys
from pathlib import Path
from typing import Optional


# ---------------------------------------------------------------------------
# Env loading — read from .env if present
# ---------------------------------------------------------------------------

def _load_dotenv(env_path: Path) -> None:
    """Minimal .env parser — sets os.environ for keys not already set."""
    if not env_path.exists():
        return
    with open(env_path) as f:
        for line in f:
            line = line.strip()
            if not line or line.startswith("#"):
                continue
            if "=" not in line:
                continue
            key, _, value = line.partition("=")
            key = key.strip()
            value = value.strip()
            # Strip inline comments and surrounding quotes
            value = value.split("#")[0].strip().strip('"').strip("'")
            if key and key not in os.environ:
                os.environ[key] = value


# ---------------------------------------------------------------------------
# Product / price definitions
# ---------------------------------------------------------------------------

PRODUCTS = [
    {
        "lookup_key_prefix": "moduledesk_premium",
        "metadata_search_key": "plan",
        "metadata_search_value": "premium",
        "name": "ModuleDesk Premium",
        "description": (
            "Professional support desk for PrestaShop sellers. "
            "Up to 6 modules, solo seat, AI composer, semantic search, and RAG drafts."
        ),
        "statement_descriptor": "MODULEDESK PREMIUM",
        "metadata": {
            "product": "moduledesk",
            "plan": "premium",
            "modules_included": "6",
            "team_members": "1",
            "ai_credits_per_month": "300",
        },
        "marketing_features": [
            "All 6 AI composer actions (~300 credits/month)",
            "Inbound & outbound translation",
            "Semantic search",
            "RAG drafts from your own documentation sources",
            "Unlimited quick-reply templates + Addons sync + categories",
            "1 seat (solo)",
            "Full order history (24 months) + CSV export",
            "Smart Priority: full 10-signal scoring",
            "Auto-sync every hour",
            "Guide generation (Beta)",
        ],
        "price": {
            "lookup_key": "moduledesk_premium_monthly_v3",
            "unit_amount": 5900,  # €59.00 in cents
            "currency": "eur",
            "tax_behavior": "exclusive",
            "recurring": {"interval": "month"},
        },
        "env_var": "STRIPE_PRICE_PREMIUM",
    },
    {
        "lookup_key_prefix": "moduledesk_max",
        "metadata_search_key": "plan",
        "metadata_search_value": "max",
        "name": "ModuleDesk MAX",
        "description": (
            "Unlimited everything for serious PrestaShop businesses. "
            "Unlimited modules, unlimited team, 1000 AI credits + bring-your-own OpenAI key, "
            "credential vault, and health monitoring."
        ),
        "statement_descriptor": "MODULEDESK MAX",
        "metadata": {
            "product": "moduledesk",
            "plan": "max",
            "modules_included": "unlimited",
            "team_members": "unlimited",
            "ai_credits_per_month": "1000",
            "byok": "true",
        },
        "marketing_features": [
            "Unlimited modules connected",
            "Unlimited team members + roles",
            "~1000 AI credits/month + bring-your-own OpenAI key (bypasses credit deduction)",
            "Credential vault with connection testing",
            "Customer website health monitoring",
            "URL safety scanning (VirusTotal)",
            "Full order history + CSV import + margin/refund filters",
            "Customer satisfaction ratings + CSV export",
            "Guide generation (Beta)",
        ],
        "price": {
            "lookup_key": "moduledesk_max_monthly_v4",
            "unit_amount": 9900,  # €99.00 in cents
            "currency": "eur",
            "tax_behavior": "exclusive",
            "recurring": {"interval": "month"},
        },
        "env_var": "STRIPE_PRICE_MAX",
    },
    {
        "lookup_key_prefix": "moduledesk_module_addon",
        "metadata_search_key": "addon_type",
        "metadata_search_value": "module_slots",
        "name": "ModuleDesk Module Add-on",
        "description": (
            "Add 5 extra module slots to your Premium plan. "
            "Each unit = +5 modules. Premium plan only."
        ),
        "statement_descriptor": "MODULEDESK ADDON",
        "metadata": {
            "product": "moduledesk",
            "addon_type": "module_slots",
            "slots_per_unit": "5",
            "plan_required": "premium",
        },
        "marketing_features": [],
        "price": {
            "lookup_key": "moduledesk_module_addon_monthly",
            "unit_amount": 500,  # €5.00 in cents
            "currency": "eur",
            "tax_behavior": "exclusive",
            "recurring": {"interval": "month"},
        },
        "env_var": "STRIPE_PRICE_MODULE_ADDON",
    },
    # ── AI credit top-up packs (2026-08-12) ───────────────────────────────────
    # Three separate products, not one price with a quantity multiplier: each
    # pack carries its own volume discount (€0.040 / €0.035 / €0.030 per credit),
    # which quantity-of-one-price cannot express. Keep TOPUP_PACKS in
    # services/billing_service.py in sync with the amounts below.
    # The old single "moduledesk_topup_200credits" price is intentionally left
    # alone if it exists — in-flight Checkout sessions still reference it.
    *[
        {
            "lookup_key_prefix": f"moduledesk_topup_{_credits}",
            "metadata_search_key": "topup_pack",
            "metadata_search_value": str(_credits),
            "name": f"ModuleDesk AI Credit Top-up — {_credits} credits",
            "description": (
                f"One-time pack of {_credits} AI credits for €{_cents / 100:.2f}. "
                "Credits are added to your organization's pool immediately after "
                "purchase and never expire. "
                "Requires an active Premium or MAX subscription."
            ),
            "statement_descriptor": "MODULEDESK TOPUP",
            "metadata": {
                "product": "moduledesk",
                "topup_type": "ai_credits",
                "topup_pack": str(_credits),
                "credits_per_pack": str(_credits),
                "subscription_required": "true",
            },
            "marketing_features": [],
            "price": {
                # _v2 suffix is REQUIRED, not cosmetic: the retired single pack
                # already owns the lookup_key "moduledesk_topup_200credits" at
                # €5, and Stripe rejects a duplicate lookup_key on a new price.
                "lookup_key": f"moduledesk_topup_{_credits}credits_v2",
                "unit_amount": _cents,
                "currency": "eur",
                "tax_behavior": "exclusive",
                # No 'recurring' key = one-time payment
            },
            "env_var": f"STRIPE_PRICE_TOPUP_{_credits}",
        }
        for _credits, _cents in ((100, 400), (200, 700), (500, 1500))
    ],
]


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------

def _find_product_by_metadata(stripe, key: str, value: str) -> Optional[object]:
    """Find an existing active product by a metadata key/value pair.

    Uses Product.list (not Product.search): the Search API is unavailable on
    this stripe lib/account and errored on every call, which made each run
    create duplicate products. Listing active products and filtering in Python
    is fully idempotent.
    """
    try:
        for product in stripe.Product.list(active=True, limit=100).auto_paging_iter():
            if product.metadata.get(key) == value and product.metadata.get("product") == "moduledesk":
                return product
    except Exception as exc:
        print(f"  [warn] product list failed: {exc}")
    return None


def _get_or_create_product(stripe, spec: dict) -> object:
    """Find an existing ModuleDesk product or create it."""
    existing = _find_product_by_metadata(
        stripe,
        spec["metadata_search_key"],
        spec["metadata_search_value"],
    )
    marketing_features = [{"name": f} for f in spec.get("marketing_features", [])]

    if existing:
        print(f"  Found existing product: {existing.id} ({existing.name})")
        # Update fields in case spec has changed
        update_kwargs: dict = {
            "name": spec["name"],
            "description": spec["description"],
            "metadata": spec["metadata"],
        }
        if marketing_features:
            update_kwargs["marketing_features"] = marketing_features
        # statement_descriptor is not supported on Product.modify; it lives on Price
        stripe.Product.modify(existing.id, **update_kwargs)
        print(f"  Updated product metadata/description.")
        return existing

    print(f"  Creating new product: {spec['name']}")
    create_kwargs: dict = {
        "name": spec["name"],
        "description": spec["description"],
        "metadata": spec["metadata"],
    }
    if marketing_features:
        create_kwargs["marketing_features"] = marketing_features
    product = stripe.Product.create(**create_kwargs)
    print(f"  Created product: {product.id}")
    return product


def _get_or_create_price(stripe, product_id: str, spec: dict) -> object:
    """
    Find the price by lookup_key; create it if absent.

    Note: Stripe prices are immutable once created (amount/currency/interval
    cannot be changed). To change a price, create a new one with a new lookup_key
    and update the env var — do not delete old prices that may be on active subs.
    """
    lookup_key = spec["lookup_key"]

    # Try to retrieve by lookup key (active only — archived prices from a wipe
    # must not be reused; transfer_lookup_key below reclaims the key cleanly).
    try:
        prices = stripe.Price.list(lookup_keys=[lookup_key], active=True, limit=1)
        if prices.data:
            price = prices.data[0]
            print(f"  Found existing price: {price.id} (lookup_key={lookup_key})")
            return price
    except Exception as exc:
        print(f"  [warn] price lookup failed: {exc}")

    print(f"  Creating new price with lookup_key={lookup_key}")
    create_kwargs: dict = {
        "product": product_id,
        "unit_amount": spec["unit_amount"],
        "currency": spec["currency"],
        "tax_behavior": spec["tax_behavior"],
        "lookup_key": lookup_key,
        "transfer_lookup_key": True,  # reassign lookup_key from old price if needed
    }
    if "recurring" in spec:
        create_kwargs["recurring"] = spec["recurring"]

    # statement_descriptor on Price (shown on receipts/invoices)
    # Note: statement_descriptor on Price is supported for one-time prices only
    # For recurring prices it goes on the Product or subscription; skip here to avoid API errors.

    price = stripe.Price.create(**create_kwargs)
    print(f"  Created price: {price.id}")
    return price


# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------

def main() -> None:
    parser = argparse.ArgumentParser(
        description="Set up ModuleDesk Stripe products and prices.",
    )
    parser.add_argument(
        "--live",
        action="store_true",
        help="Allow running against a live Stripe key (sk_live_...). "
             "Without this flag the script refuses to run if a live key is detected.",
    )
    args = parser.parse_args()

    # Load .env from project root (two levels up from scripts/)
    project_root = Path(__file__).resolve().parent.parent
    _load_dotenv(project_root / ".env")

    secret_key = os.getenv("STRIPE_SECRET_KEY", "").strip()
    if not secret_key:
        print("ERROR: STRIPE_SECRET_KEY is not set in .env or environment.")
        print("Set it and re-run.")
        sys.exit(1)

    is_live_key = secret_key.startswith("sk_live_")
    if is_live_key and not args.live:
        print("ERROR: STRIPE_SECRET_KEY looks like a live key (sk_live_...).")
        print("Pass --live to confirm you want to create products in live mode.")
        print("Running without --live is only for test keys (sk_test_...).")
        sys.exit(1)

    mode = "LIVE" if is_live_key else "TEST"
    print(f"\nModuleDesk Stripe Product Setup — {mode} MODE")
    print("=" * 55)

    try:
        import stripe
    except ImportError:
        print("ERROR: stripe package not installed.")
        print("Run: venv/bin/pip install stripe")
        sys.exit(1)

    stripe.api_key = secret_key

    results: dict[str, str] = {}  # env_var -> price_id

    for spec in PRODUCTS:
        print(f"\n--- {spec['name']} ---")
        product = _get_or_create_product(stripe, spec)
        price = _get_or_create_price(stripe, product.id, spec["price"])
        results[spec["env_var"]] = price.id

    # Print .env lines
    print("\n" + "=" * 55)
    print("Paste these lines into your .env file:\n")
    for env_var, price_id in results.items():
        print(f"{env_var}={price_id}")

    print("\nNext steps:")
    print("1. Paste the lines above into .env")
    print("2. Set STRIPE_WEBHOOK_SECRET=whsec_... (from Stripe Dashboard → Webhooks)")
    print("3. sudo systemctl restart supporthub")
    print()


if __name__ == "__main__":
    main()
