#!/usr/bin/env python
"""Provision the limited-slot founding pricing campaign and its dedicated Stripe Prices.

Each --price creates one Stripe Price on the plan's existing Stripe product, records it in
``pricing_campaign_prices`` and registers it as a non-current ``plan_price_versions`` row so
webhooks can map a founder's subscription back to its plan. The catalog sync never rotates
these Prices, so founders keep their amount even when catalog prices change.

Dry-run by default: nothing is written to Stripe or the database without --apply.

Usage:
  python -m scripts.create_founding_offer --max 1000 \\
      --price base:month=2000 --price base:year=<cents> \\
      --price pro:month=6000  --price pro:year=<cents> [--apply]

Amounts are minor units (cents). Re-running with the same amounts is a no-op; an existing
plan/interval with a different amount is refused. Requires DATABASE_URL and STRIPE_SECRET_KEY.
"""
from __future__ import annotations
import argparse
import asyncio
import os
import sys

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))


def parse_price(raw: str) -> tuple[str, str, int]:
    try:
        target, amount = raw.split("=", 1)
        slug, interval = target.split(":", 1)
        minor = int(amount)
    except ValueError as exc:
        raise argparse.ArgumentTypeError(f"expected <plan_slug>:<month|year>=<cents>, got {raw!r}") from exc
    if interval not in ("month", "year"):
        raise argparse.ArgumentTypeError(f"interval must be month or year, got {interval!r}")
    if minor <= 0:
        raise argparse.ArgumentTypeError("amount must be positive")
    return slug.strip().lower(), interval, minor


async def run(args: argparse.Namespace) -> int:
    from src.core.database import async_session_factory
    from src.domain.admin.repository import PlanPriceVersionRepository, SubscriptionPlanRepository
    from src.domain.billing.campaign_repository import PricingCampaignRepository
    from src.domain.billing.campaign_service import provision_campaign_price
    from src.integrations.stripe_client import StripeClientImpl

    async with async_session_factory() as db:
        plan_repo = SubscriptionPlanRepository(db)
        repo = PricingCampaignRepository(db)

        plans = {}
        for slug, interval, minor in args.price:
            plan = plans.get(slug) or await plan_repo.get_by_slug(slug)
            if not plan:
                available = ", ".join(p.slug for p in await plan_repo.list_all(active_only=False))
                print(f"Unknown plan slug {slug!r}. Available: {available}")
                return 1
            plans[slug] = plan
            print(
                f"  {plan.slug}/{interval}: catalog {getattr(plan, 'price_monthly' if interval == 'month' else 'price_yearly')}"
                f" -> campaign {minor}"
            )

        campaign = await repo.get_by_slug(args.campaign)
        if campaign:
            print(f"Campaign {args.campaign!r} exists (max_redemptions={campaign.max_redemptions}).")
            if campaign.max_redemptions != args.max:
                print(f"  note: --max {args.max} ignored; the existing cap is kept")
        else:
            print(f"Campaign {args.campaign!r} will be created (max_redemptions={args.max}).")

        if not args.apply:
            print("\nDry run only. Re-run with --apply to create the Stripe Prices and records.")
            return 0

        if not campaign:
            campaign = await repo.create_campaign(
                slug=args.campaign, name=args.name, max_redemptions=args.max
            )
            await db.commit()

        stripe = StripeClientImpl()
        price_versions = PlanPriceVersionRepository(db)
        for slug, interval, minor in args.price:
            row, created = await provision_campaign_price(
                repo=repo,
                price_version_repo=price_versions,
                stripe=stripe,
                campaign=campaign,
                plan=plans[slug],
                interval=interval,
                amount_minor=minor,
            )
            await db.commit()  # commit per price so a later failure never orphans a recorded Stripe Price
            print(f"{'created' if created else 'exists '} {slug}/{interval}: {row.stripe_price_id}")
    return 0


def main() -> int:
    parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument("--campaign", default="founding", help="campaign slug (default: founding)")
    parser.add_argument("--name", default="Founding Customer", help="display name used in Stripe nicknames")
    parser.add_argument("--max", type=int, required=True, help="total redemptions shared across all prices")
    parser.add_argument("--price", type=parse_price, action="append", required=True,
                        metavar="PLAN:INTERVAL=CENTS", help="repeatable")
    parser.add_argument("--apply", action="store_true", help="write to Stripe and the database")
    args = parser.parse_args()
    if args.max < 1:
        parser.error("--max must be at least 1")
    return asyncio.run(run(args))


if __name__ == "__main__":
    raise SystemExit(main())
