"""
Create or sync Paddle products, prices, and webhook notification destination.

Commands:
    uv run python -m padelnomics.scripts.setup_paddle          # first-time: create products + webhook
    uv run python -m padelnomics.scripts.setup_paddle --sync   # re-populate DB from existing Paddle products
"""

import logging
import os
import re
import sqlite3
import sys
from pathlib import Path

from dotenv import load_dotenv
from paddle_billing import Client as PaddleClient
from paddle_billing import Environment, Options
from paddle_billing.Entities.Events.EventTypeName import EventTypeName
from paddle_billing.Entities.NotificationSettings.NotificationSettingType import (
    NotificationSettingType,
)
from paddle_billing.Entities.Shared import CurrencyCode, Money, TaxCategory
from paddle_billing.Resources.NotificationSettings.Operations import CreateNotificationSetting
from paddle_billing.Resources.Prices.Operations import CreatePrice
from paddle_billing.Resources.Products.Operations import CreateProduct, ListProducts
from paddle_billing.Resources.Products.Operations.List.Includes import Includes

logger = logging.getLogger(__name__)

load_dotenv()

PADDLE_API_KEY = os.getenv("PADDLE_API_KEY", "")
PADDLE_ENVIRONMENT = os.getenv("PADDLE_ENVIRONMENT", "sandbox")
DATABASE_PATH = os.getenv("DATABASE_PATH", "data/app.db")
BASE_URL = os.getenv("BASE_URL", "http://localhost:5000")

if not PADDLE_API_KEY:
    logging.basicConfig(level=logging.INFO, format="%(levelname)-8s %(message)s")
    logger.error("Set PADDLE_API_KEY in .env first")
    sys.exit(1)


# Maps our internal key -> product name in Paddle.
# The name is used to match existing products on sync.
PRODUCTS = [
    # NOTE: Basic tier is free — no Paddle subscription product needed.
    # Suppliers select Basic during signup without a payment flow.
    # These entries are kept as dead/legacy references only; do not create them.
    # {
    #     "key": "supplier_basic_monthly",
    #     "name": "Supplier Basic (Monthly)",
    #     "price": 0,
    #     ...
    # },
    # Subscriptions — Growth tier (existing monthly + new yearly)
    {
        "key": "supplier_growth",
        "name": "Supplier Growth",
        "price": 19900,
        "currency": CurrencyCode.EUR,
        "interval": "month",
        "billing_type": "subscription",
    },
    {
        "key": "supplier_growth_yearly",
        "name": "Supplier Growth (Yearly)",
        "price": 179900,
        "currency": CurrencyCode.EUR,
        "interval": "year",
        "billing_type": "subscription",
    },
    # Subscriptions — Pro tier (existing monthly + new yearly)
    {
        "key": "supplier_pro",
        "name": "Supplier Pro",
        "price": 49900,
        "currency": CurrencyCode.EUR,
        "interval": "month",
        "billing_type": "subscription",
    },
    {
        "key": "supplier_pro_yearly",
        "name": "Supplier Pro (Yearly)",
        "price": 449900,
        "currency": CurrencyCode.EUR,
        "interval": "year",
        "billing_type": "subscription",
    },
    # Boost add-ons (subscriptions)
    {
        "key": "boost_logo",
        "name": "Boost: Logo",
        "price": 2900,
        "currency": CurrencyCode.EUR,
        "interval": "month",
        "billing_type": "subscription",
    },
    {
        "key": "boost_highlight",
        "name": "Boost: Highlight",
        "price": 3900,
        "currency": CurrencyCode.EUR,
        "interval": "month",
        "billing_type": "subscription",
    },
    {
        "key": "boost_verified",
        "name": "Boost: Verified Badge",
        "price": 4900,
        "currency": CurrencyCode.EUR,
        "interval": "month",
        "billing_type": "subscription",
    },
    {
        "key": "boost_card_color",
        "name": "Boost: Custom Card Color",
        "price": 5900,
        "currency": CurrencyCode.EUR,
        "interval": "month",
        "billing_type": "subscription",
    },
    # One-time boosts
    {
        "key": "boost_sticky_week",
        "name": "Boost: Sticky Top 1 Week",
        "price": 7900,
        "currency": CurrencyCode.EUR,
        "billing_type": "one_time",
    },
    {
        "key": "boost_sticky_month",
        "name": "Boost: Sticky Top 1 Month",
        "price": 19900,
        "currency": CurrencyCode.EUR,
        "billing_type": "one_time",
    },
    # Credit packs
    {
        "key": "credits_25",
        "name": "Credit Pack 25",
        "price": 9900,
        "currency": CurrencyCode.EUR,
        "billing_type": "one_time",
    },
    {
        "key": "credits_50",
        "name": "Credit Pack 50",
        "price": 17900,
        "currency": CurrencyCode.EUR,
        "billing_type": "one_time",
    },
    {
        "key": "credits_100",
        "name": "Credit Pack 100",
        "price": 32900,
        "currency": CurrencyCode.EUR,
        "billing_type": "one_time",
    },
    {
        "key": "credits_250",
        "name": "Credit Pack 250",
        "price": 74900,
        "currency": CurrencyCode.EUR,
        "billing_type": "one_time",
    },
    # PDF product
    {
        "key": "business_plan",
        "name": "Padel Business Plan (PDF)",
        "price": 14900,
        "currency": CurrencyCode.EUR,
        "billing_type": "one_time",
    },
    # Planner subscriptions
    {
        "key": "starter",
        "name": "Planner Starter",
        "price": 1900,
        "currency": CurrencyCode.EUR,
        "interval": "month",
        "billing_type": "subscription",
    },
    {
        "key": "pro",
        "name": "Planner Pro",
        "price": 4900,
        "currency": CurrencyCode.EUR,
        "interval": "month",
        "billing_type": "subscription",
    },
]

# Build name -> spec lookup
_PRODUCT_BY_NAME = {p["name"]: p for p in PRODUCTS}


def _open_db():
    db_path = DATABASE_PATH
    if not Path(db_path).exists():
        logger.error("Database not found at %s. Run migrations first.", db_path)
        sys.exit(1)
    conn = sqlite3.connect(db_path)
    conn.execute("PRAGMA journal_mode=WAL")
    conn.execute("PRAGMA foreign_keys=ON")
    return conn


def _write_product(conn, key, product_id, price_id, name, price_cents, billing_type):
    conn.execute(
        """INSERT OR REPLACE INTO paddle_products
           (key, paddle_product_id, paddle_price_id, name, price_cents, currency, billing_type)
           VALUES (?, ?, ?, ?, ?, ?, ?)""",
        (key, product_id, price_id, name, price_cents, "EUR", billing_type),
    )


def sync(paddle, conn):
    """Fetch existing products from Paddle and re-populate paddle_products table."""
    logger.info("Syncing products from Paddle (%s)...", PADDLE_ENVIRONMENT)

    products = paddle.products.list(ListProducts(includes=[Includes.Prices]))

    matched = 0
    for product in products:
        spec = _PRODUCT_BY_NAME.get(product.name)
        if not spec:
            continue
        if not product.prices or len(product.prices) == 0:
            logger.warning("  SKIP %s: no prices on %s", spec["key"], product.id)
            continue

        # Use the first active price
        price = product.prices[0]
        _write_product(
            conn, spec["key"], product.id, price.id,
            spec["name"], spec["price"], spec["billing_type"],
        )
        matched += 1
        logger.info("  %s: %s / %s", spec["key"], product.id, price.id)

    conn.commit()

    if matched == 0:
        logger.warning("No matching products found in Paddle. Run without --sync first.")
    else:
        logger.info("%s/%s products synced to DB", matched, len(PRODUCTS))


def create(paddle, conn):
    """Create new products and prices in Paddle, write to DB, set up webhook."""
    logger.info("Creating products in %s...", PADDLE_ENVIRONMENT)

    for spec in PRODUCTS:
        product = paddle.products.create(CreateProduct(
            name=spec["name"],
            tax_category=TaxCategory.Standard,
        ))
        logger.info("  Product: %s -> %s", spec["name"], product.id)

        price_kwargs = {
            "description": spec["name"],
            "product_id": product.id,
            "unit_price": Money(str(spec["price"]), spec["currency"]),
        }

        if spec["billing_type"] == "subscription":
            from paddle_billing.Entities.Shared import Duration, Interval
            if spec.get("interval") == "year":
                price_kwargs["billing_cycle"] = Duration(interval=Interval.Year, frequency=1)
            else:
                price_kwargs["billing_cycle"] = Duration(interval=Interval.Month, frequency=1)

        price = paddle.prices.create(CreatePrice(**price_kwargs))
        logger.info("  Price:   %s = %s", spec["key"], price.id)

        _write_product(
            conn, spec["key"], product.id, price.id,
            spec["name"], spec["price"], spec["billing_type"],
        )

    conn.commit()
    logger.info("All products written to DB")

    # -- Notification destination (webhook) -----------------------------------

    webhook_url = f"{BASE_URL}/billing/webhook/paddle"

    subscribed_events = [
        EventTypeName.SubscriptionActivated,
        EventTypeName.SubscriptionUpdated,
        EventTypeName.SubscriptionCanceled,
        EventTypeName.SubscriptionPastDue,
        EventTypeName.TransactionCompleted,
    ]

    logger.info("Creating webhook notification destination...")
    logger.info("  URL: %s", webhook_url)

    notification_setting = paddle.notification_settings.create(
        CreateNotificationSetting(
            description=f"Padelnomics {PADDLE_ENVIRONMENT}",
            destination=webhook_url,
            subscribed_events=subscribed_events,
            type=NotificationSettingType.Url,
            include_sensitive_fields=True,
            api_version=1,
        )
    )

    webhook_secret = notification_setting.endpoint_secret_key
    logger.info("  ID:     %s", notification_setting.id)
    logger.info("  Secret: %s", webhook_secret)

    env_path = Path(".env")
    env_vars = {
        "PADDLE_WEBHOOK_SECRET": webhook_secret,
        "PADDLE_NOTIFICATION_SETTING_ID": notification_setting.id,
    }
    if env_path.exists():
        env_text = env_path.read_text()
        for key, value in env_vars.items():
            pattern = rf"^{key}=.*$"
            replacement = f"{key}={value}"
            if re.search(pattern, env_text, flags=re.MULTILINE):
                env_text = re.sub(pattern, replacement, env_text, flags=re.MULTILINE)
            else:
                env_text = env_text.rstrip("\n") + f"\n{replacement}\n"
        env_path.write_text(env_text)
        logger.info("PADDLE_WEBHOOK_SECRET and PADDLE_NOTIFICATION_SETTING_ID written to .env")
    else:
        logger.info("Add to .env:")
        for key, value in env_vars.items():
            logger.info("  %s=%s", key, value)

    logger.info("Done. dev_run.sh will start ngrok and update the webhook URL automatically.")


def main():
    env = Environment.SANDBOX if PADDLE_ENVIRONMENT == "sandbox" else Environment.PRODUCTION
    paddle = PaddleClient(PADDLE_API_KEY, options=Options(env))
    conn = _open_db()

    try:
        if "--sync" in sys.argv:
            sync(paddle, conn)
        else:
            create(paddle, conn)
    finally:
        conn.close()


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO, format="%(levelname)-8s %(message)s")
    main()
