"""
Stripe Sandbox Integration Test — verifies all products work end-to-end.

Creates multiple test customers with different personas, tests:
- Checkout session creation for every product
- Subscription creation + cancellation lifecycle
- One-time payment intents
- Price/product consistency

Run: uv run python scripts/test_stripe_sandbox.py
"""

import os
import sys
import time

from dotenv import load_dotenv

load_dotenv()

import stripe

STRIPE_SECRET_KEY = os.getenv("STRIPE_SECRET_KEY", "") or os.getenv("STRIPE_API_PRIVATE_KEY", "")
if not STRIPE_SECRET_KEY:
    print("ERROR: STRIPE_SECRET_KEY / STRIPE_API_PRIVATE_KEY not set in .env")
    sys.exit(1)

stripe.api_key = STRIPE_SECRET_KEY
stripe.max_network_retries = 2

BASE_URL = os.getenv("BASE_URL", "http://localhost:5000")

# ═══════════════════════════════════════════════════════════
# Expected product catalog — must match setup_stripe.py
# ═══════════════════════════════════════════════════════════

EXPECTED_PRODUCTS = {
    "Supplier Growth": {"price_cents": 19900, "billing": "subscription", "interval": "month"},
    "Supplier Growth (Yearly)": {"price_cents": 179900, "billing": "subscription", "interval": "year"},
    "Supplier Pro": {"price_cents": 49900, "billing": "subscription", "interval": "month"},
    "Supplier Pro (Yearly)": {"price_cents": 449900, "billing": "subscription", "interval": "year"},
    "Boost: Logo": {"price_cents": 2900, "billing": "subscription", "interval": "month"},
    "Boost: Highlight": {"price_cents": 3900, "billing": "subscription", "interval": "month"},
    "Boost: Verified Badge": {"price_cents": 4900, "billing": "subscription", "interval": "month"},
    "Boost: Custom Card Color": {"price_cents": 5900, "billing": "subscription", "interval": "month"},
    "Boost: Sticky Top 1 Week": {"price_cents": 7900, "billing": "one_time"},
    "Boost: Sticky Top 1 Month": {"price_cents": 19900, "billing": "one_time"},
    "Credit Pack 25": {"price_cents": 9900, "billing": "one_time"},
    "Credit Pack 50": {"price_cents": 17900, "billing": "one_time"},
    "Credit Pack 100": {"price_cents": 32900, "billing": "one_time"},
    "Credit Pack 250": {"price_cents": 74900, "billing": "one_time"},
    "Padel Business Plan (PDF)": {"price_cents": 14900, "billing": "one_time"},
    "Planner Starter": {"price_cents": 1900, "billing": "subscription", "interval": "month"},
    "Planner Pro": {"price_cents": 4900, "billing": "subscription", "interval": "month"},
}

# Test customer personas
TEST_CUSTOMERS = [
    {"email": "planner-starter@sandbox.padelnomics.com", "name": "Anna Planner (Starter)"},
    {"email": "planner-pro@sandbox.padelnomics.com", "name": "Ben Planner (Pro)"},
    {"email": "supplier-growth@sandbox.padelnomics.com", "name": "Carlos Supplier (Growth)"},
    {"email": "supplier-pro@sandbox.padelnomics.com", "name": "Diana Supplier (Pro)"},
    {"email": "one-time-buyer@sandbox.padelnomics.com", "name": "Eva Buyer (Credits+Boosts)"},
]

passed = 0
failed = 0
errors = []


def ok(msg):
    global passed
    passed += 1
    print(f"  ✓ {msg}")


def fail(msg):
    global failed
    failed += 1
    errors.append(msg)
    print(f"  ✗ {msg}")


def section(title):
    print(f"\n{'─' * 60}")
    print(f"  {title}")
    print(f"{'─' * 60}")


# ═══════════════════════════════════════════════════════════
# Phase 1: Verify all products and prices exist
# ═══════════════════════════════════════════════════════════

section("Phase 1: Product & Price Verification")

products = list(stripe.Product.list(limit=100, active=True).auto_paging_iter())
product_map = {}  # name -> {product_id, price_id, price_amount, price_type, interval}

for product in products:
    prices = stripe.Price.list(product=product.id, active=True, limit=1)
    if not prices.data:
        continue
    price = prices.data[0]
    product_map[product.name] = {
        "product_id": product.id,
        "price_id": price.id,
        "price_amount": price.unit_amount,
        "price_type": price.type,
        "interval": price.recurring.interval if price.recurring else None,
    }

for name, expected in EXPECTED_PRODUCTS.items():
    if name not in product_map:
        fail(f"MISSING product: {name}")
        continue

    actual = product_map[name]
    if actual["price_amount"] != expected["price_cents"]:
        fail(f"{name}: price {actual['price_amount']} != expected {expected['price_cents']}")
    elif expected["billing"] == "subscription" and actual["price_type"] != "recurring":
        fail(f"{name}: expected recurring, got {actual['price_type']}")
    elif expected["billing"] == "one_time" and actual["price_type"] != "one_time":
        fail(f"{name}: expected one_time, got {actual['price_type']}")
    elif expected.get("interval") and actual["interval"] != expected["interval"]:
        fail(f"{name}: interval {actual['interval']} != expected {expected['interval']}")
    else:
        ok(f"{name}: €{actual['price_amount']/100:.2f} ({actual['price_type']}"
           f"{', ' + actual['interval'] if actual['interval'] else ''})")

extra_products = set(product_map.keys()) - set(EXPECTED_PRODUCTS.keys())
if extra_products:
    print(f"\n  ℹ Extra products in Stripe (not in catalog): {extra_products}")


# ═══════════════════════════════════════════════════════════
# Phase 2: Create test customers (idempotent)
# ═══════════════════════════════════════════════════════════

section("Phase 2: Create Test Customers")

customer_ids = {}  # email -> customer_id

for persona in TEST_CUSTOMERS:
    existing = stripe.Customer.list(email=persona["email"], limit=1)
    if existing.data:
        cus = existing.data[0]
        ok(f"Reusing: {persona['name']} ({cus.id})")
    else:
        cus = stripe.Customer.create(
            email=persona["email"],
            name=persona["name"],
            metadata={"test": "true", "persona": persona["name"]},
        )
        ok(f"Created: {persona['name']} ({cus.id})")
    customer_ids[persona["email"]] = cus.id


# ═══════════════════════════════════════════════════════════
# Phase 3: Test Checkout Sessions for every product
# ═══════════════════════════════════════════════════════════

section("Phase 3: Checkout Session Creation (all products)")

success_url = f"{BASE_URL}/billing/success?session_id={{CHECKOUT_SESSION_ID}}"
cancel_url = f"{BASE_URL}/billing/pricing"

# Use the first customer for checkout tests
checkout_customer = customer_ids["planner-starter@sandbox.padelnomics.com"]

for name, info in product_map.items():
    if name not in EXPECTED_PRODUCTS:
        continue

    mode = "subscription" if info["price_type"] == "recurring" else "payment"
    try:
        session = stripe.checkout.Session.create(
            mode=mode,
            customer=checkout_customer,
            line_items=[{"price": info["price_id"], "quantity": 1}],
            metadata={"user_id": "999", "plan": name, "test": "true"},
            success_url=success_url,
            cancel_url=cancel_url,
        )
        ok(f"Checkout ({mode}): {name} -> {session.id[:30]}...")
    except stripe.StripeError as e:
        fail(f"Checkout FAILED for {name}: {e.user_message or str(e)}")


# ═══════════════════════════════════════════════════════════
# Phase 4: Subscription lifecycle tests (per persona)
# ═══════════════════════════════════════════════════════════

section("Phase 4: Subscription Lifecycle Tests")

created_subs = []

# Cache: customer_id -> payment_method_id
_customer_pms = {}


def _ensure_payment_method(cus_id):
    """Create and attach a test Visa card to a customer (cached)."""
    if cus_id in _customer_pms:
        return _customer_pms[cus_id]

    pm = stripe.PaymentMethod.create(type="card", card={"token": "tok_visa"})
    stripe.PaymentMethod.attach(pm.id, customer=cus_id)
    stripe.Customer.modify(
        cus_id,
        invoice_settings={"default_payment_method": pm.id},
    )
    _customer_pms[cus_id] = pm.id
    return pm.id


def test_subscription(customer_email, product_name, user_id, extra_metadata=None):
    """Create a subscription, verify it's active, then cancel it."""
    cus_id = customer_ids[customer_email]
    info = product_map.get(product_name)
    if not info:
        fail(f"Product not found: {product_name}")
        return

    metadata = {"user_id": str(user_id), "plan": product_name, "test": "true"}
    if extra_metadata:
        metadata.update(extra_metadata)

    pm_id = _ensure_payment_method(cus_id)

    # Create subscription
    sub = stripe.Subscription.create(
        customer=cus_id,
        items=[{"price": info["price_id"]}],
        metadata=metadata,
        default_payment_method=pm_id,
    )
    created_subs.append(sub.id)

    if sub.status == "active":
        ok(f"Sub created: {product_name} for {customer_email} -> {sub.id} (active)")
    else:
        fail(f"Sub status unexpected: {product_name} -> {sub.status} (expected active)")

    # Verify subscription items
    items = sub["items"]["data"]
    if len(items) == 1 and items[0]["price"]["id"] == info["price_id"]:
        ok(f"Sub items correct: price={info['price_id'][:20]}...")
    else:
        fail(f"Sub items mismatch for {product_name}")

    # Cancel at period end
    updated = stripe.Subscription.modify(sub.id, cancel_at_period_end=True)
    if updated.cancel_at_period_end:
        ok(f"Cancel scheduled: {product_name} (cancel_at_period_end=True)")
    else:
        fail(f"Cancel failed for {product_name}")

    # Immediately cancel to clean up
    deleted = stripe.Subscription.cancel(sub.id)
    if deleted.status == "canceled":
        ok(f"Cancelled: {product_name} -> {deleted.status}")
    else:
        fail(f"Final cancel status: {product_name} -> {deleted.status}")


# Planner Starter
test_subscription(
    "planner-starter@sandbox.padelnomics.com", "Planner Starter", user_id=101,
)

# Planner Pro
test_subscription(
    "planner-pro@sandbox.padelnomics.com", "Planner Pro", user_id=102,
)

# Supplier Growth (monthly)
test_subscription(
    "supplier-growth@sandbox.padelnomics.com", "Supplier Growth", user_id=103,
    extra_metadata={"supplier_id": "201"},
)

# Supplier Pro (monthly)
test_subscription(
    "supplier-pro@sandbox.padelnomics.com", "Supplier Pro", user_id=104,
    extra_metadata={"supplier_id": "202"},
)


# ═══════════════════════════════════════════════════════════
# Phase 5: One-time payment tests
# ═══════════════════════════════════════════════════════════

section("Phase 5: One-Time Payment Tests")

buyer_id = customer_ids["one-time-buyer@sandbox.padelnomics.com"]
buyer_pm = _ensure_payment_method(buyer_id)

ONE_TIME_PRODUCTS = [
    "Credit Pack 25",
    "Credit Pack 50",
    "Credit Pack 100",
    "Credit Pack 250",
    "Boost: Sticky Top 1 Week",
    "Boost: Sticky Top 1 Month",
    "Padel Business Plan (PDF)",
]

for product_name in ONE_TIME_PRODUCTS:
    info = product_map.get(product_name)
    if not info:
        fail(f"Product not found: {product_name}")
        continue

    try:
        pi = stripe.PaymentIntent.create(
            amount=info["price_amount"],
            currency="eur",
            customer=buyer_id,
            payment_method=buyer_pm,
            confirm=True,
            automatic_payment_methods={"enabled": True, "allow_redirects": "never"},
            metadata={
                "user_id": "105",
                "supplier_id": "203",
                "plan": product_name,
                "test": "true",
            },
        )
        if pi.status == "succeeded":
            ok(f"Payment: {product_name} -> €{info['price_amount']/100:.2f} ({pi.id[:25]}...)")
        else:
            fail(f"Payment status: {product_name} -> {pi.status}")
    except stripe.StripeError as e:
        fail(f"Payment FAILED for {product_name}: {e.user_message or str(e)}")


# ═══════════════════════════════════════════════════════════
# Phase 6: Boost subscription add-ons
# ═══════════════════════════════════════════════════════════

section("Phase 6: Boost Add-on Subscriptions")

BOOST_PRODUCTS = [
    "Boost: Logo",
    "Boost: Highlight",
    "Boost: Verified Badge",
    "Boost: Custom Card Color",
]

boost_customer = customer_ids["supplier-pro@sandbox.padelnomics.com"]
boost_pm = _ensure_payment_method(boost_customer)

for product_name in BOOST_PRODUCTS:
    info = product_map.get(product_name)
    if not info:
        fail(f"Product not found: {product_name}")
        continue

    try:
        sub = stripe.Subscription.create(
            customer=boost_customer,
            items=[{"price": info["price_id"]}],
            metadata={
                "user_id": "104",
                "supplier_id": "202",
                "plan": product_name,
                "test": "true",
            },
            default_payment_method=boost_pm,
        )
        created_subs.append(sub.id)

        if sub.status == "active":
            ok(f"Boost sub: {product_name} -> €{info['price_amount']/100:.2f}/mo ({sub.id[:25]}...)")
        else:
            fail(f"Boost sub status: {product_name} -> {sub.status}")

        # Clean up
        stripe.Subscription.cancel(sub.id)
    except stripe.StripeError as e:
        fail(f"Boost sub FAILED for {product_name}: {e.user_message or str(e)}")


# ═══════════════════════════════════════════════════════════
# Phase 7: Billing Portal access
# ═══════════════════════════════════════════════════════════

section("Phase 7: Billing Portal")

try:
    portal = stripe.billing_portal.Session.create(
        customer=checkout_customer,
        return_url=f"{BASE_URL}/billing/success",
    )
    ok(f"Portal URL generated: {portal.url[:50]}...")
except stripe.StripeError as e:
    fail(f"Portal creation failed: {e.user_message or str(e)}")


# ═══════════════════════════════════════════════════════════
# Summary
# ═══════════════════════════════════════════════════════════

section("RESULTS")

total = passed + failed
print(f"\n  {passed}/{total} passed, {failed} failed\n")

if errors:
    print("  Failures:")
    for err in errors:
        print(f"    - {err}")
    print()

# Customer summary
print("  Test customers in sandbox:")
for persona in TEST_CUSTOMERS:
    cid = customer_ids.get(persona["email"], "?")
    print(f"    {persona['name']}: {cid}")

print()
sys.exit(1 if failed else 0)
