/**
 * scripts/setup-stripe-products.js
 *
 * One-time automated Stripe onboarding. Creates:
 *   - 4 products: Standard, Pro, Agency, Credit Pack
 *   - 7 prices: monthly + annual for each subscription plan,
 *               1 one-time for the credit pack
 *   - 1 webhook endpoint at https://takedowns.vesamuni.com/webhook/stripe
 *     listening to: checkout.session.completed, payment_intent.succeeded,
 *                   customer.subscription.{created,updated,deleted},
 *                   invoice.{payment_failed,paid}
 *
 * Idempotent — safe to re-run. Existing products are matched by name;
 * existing prices by lookup_key; existing webhook endpoints by URL.
 *
 * Usage:
 *   STRIPE_SECRET_KEY=rk_live_xxx node scripts/setup-stripe-products.js              # LIVE: create
 *   STRIPE_SECRET_KEY=rk_live_xxx node scripts/setup-stripe-products.js --dry-run    # report only
 *
 * Output: a copy-paste-ready .env block with all 7 price IDs + webhook secret.
 */

'use strict';

const Stripe = require('stripe');

const WEBHOOK_URL = process.env.STRIPE_WEBHOOK_URL || 'https://takedowns.vesamuni.com/webhook/stripe';
const DRY_RUN     = process.argv.includes('--dry-run');

// Prices match config/plans.js exactly. Recurring prices use usage_type=licensed
// so Agency seats are billed via subscription quantity, not separate prices.
const PLANS = [
  {
    productKey: 'standard',
    name: 'NCII Shield — Standard',
    description: 'NCII Shield Standard plan: 5 active cases, 100 scans/mo, 50 takedowns/mo.',
    metadata: { plan: 'standard' },
    prices: [
      { envKey: 'STRIPE_PRICE_STANDARD',        lookupKey: 'ncii_shield_standard_monthly',  amount:  2900, currency: 'usd', interval: 'month', nickname: 'Standard monthly'  },
      { envKey: 'STRIPE_PRICE_STANDARD_ANNUAL', lookupKey: 'ncii_shield_standard_annual',   amount: 29000, currency: 'usd', interval: 'year',  nickname: 'Standard annual (2 mo free)' },
    ],
  },
  {
    productKey: 'pro',
    name: 'NCII Shield — Pro',
    description: 'NCII Shield Pro plan: 20 active cases, 500 scans/mo, 200 takedowns/mo, 3 users.',
    metadata: { plan: 'pro' },
    prices: [
      { envKey: 'STRIPE_PRICE_PRO',        lookupKey: 'ncii_shield_pro_monthly',  amount:  7900, currency: 'usd', interval: 'month', nickname: 'Pro monthly'  },
      { envKey: 'STRIPE_PRICE_PRO_ANNUAL', lookupKey: 'ncii_shield_pro_annual',   amount: 79000, currency: 'usd', interval: 'year',  nickname: 'Pro annual (2 mo free)' },
    ],
  },
  {
    productKey: 'agency',
    name: 'NCII Shield — Agency (per-seat)',
    description: 'NCII Shield Agency plan: per-seat team workspace with REST API access. Min 3 seats.',
    metadata: { plan: 'agency' },
    prices: [
      // Agency is per-seat: ONE recurring price, seats set by quantity on subscription.
      { envKey: 'STRIPE_PRICE_AGENCY',        lookupKey: 'ncii_shield_agency_monthly',  amount:  7900, currency: 'usd', interval: 'month', nickname: 'Agency per-seat monthly' },
      { envKey: 'STRIPE_PRICE_AGENCY_ANNUAL', lookupKey: 'ncii_shield_agency_annual',   amount: 79000, currency: 'usd', interval: 'year',  nickname: 'Agency per-seat annual (2 mo free)' },
    ],
  },
  {
    productKey: 'credit_pack_small',
    name: 'NCII Shield — Credit Pack (5 scans + 3 takedowns)',
    description: 'One-time credit pack: 5 scan credits + 3 takedown credits. Never expires, stacks with any plan.',
    metadata: { plan: 'credit_pack_small' },
    prices: [
      // No `recurring` → mode='payment' (single charge), as required by routes/billing.js
      { envKey: 'STRIPE_PRICE_CREDIT_PACK_SMALL', lookupKey: 'ncii_shield_credit_pack_small', amount: 300, currency: 'usd', oneTime: true, nickname: 'Credit Pack (one-time)' },
    ],
  },
];

const WEBHOOK_EVENTS = [
  'checkout.session.completed',
  'checkout.session.async_payment_succeeded',
  'payment_intent.succeeded',
  'customer.subscription.created',
  'customer.subscription.updated',
  'customer.subscription.deleted',
  'invoice.payment_failed',
  'invoice.paid',
];

function header(title) {
  console.log('\n' + '='.repeat(72));
  console.log(' ' + title);
  console.log('='.repeat(72));
}

function fmtUSD(cents) { return '$' + (cents / 100).toFixed(2); }

// Stripe tax code for SaaS — required for automatic_tax to compute correctly.
// txcd_10202000 = "Software as a service (SaaS) — business use"
// txcd_10000000 = "General — Services" (fallback if Stripe rejects SaaS code)
const SAAS_TAX_CODE = 'txcd_10202000';

async function findProductByName(stripe, name) {
  const all = await stripe.products.list({ limit: 100, active: true });
  return all.data.find(p => p.name === name) || null;
}

async function findPriceByLookupKey(stripe, lookupKey) {
  const all = await stripe.prices.list({ lookup_keys: [lookupKey], limit: 1, active: true });
  return all.data[0] || null;
}

async function findWebhookByUrl(stripe, url) {
  const all = await stripe.webhookEndpoints.list({ limit: 100 });
  return all.data.find(w => w.url === url) || null;
}

async function main() {
  if (!process.env.STRIPE_SECRET_KEY) {
    console.error('STRIPE_SECRET_KEY env var is required.');
    process.exit(1);
  }
  // Accept either an unrestricted live secret key (sk_live_…) or a
  // restricted live key (rk_live_…). startsWith takes a single string,
  // so we test each prefix explicitly.
  const LIVE_KEY_PREFIXES = ['sk_live_', 'rk_live_'];
  const key = process.env.STRIPE_SECRET_KEY;
  if (!DRY_RUN && !LIVE_KEY_PREFIXES.some(p => key.startsWith(p))) {
    console.error('Refusing to run against a non-live key without --dry-run.');
    console.error('Pass --dry-run if you want a report-only run.');
    process.exit(1);
  }
  const stripe = new Stripe(process.env.STRIPE_SECRET_KEY, { apiVersion: '2026-06-24.dahlia' });

  // ---- 0. Verify credentials + report account ----
  header('Verifying Stripe credentials');
  let acct;
  try {
    acct = await stripe.account.retrieve();
  } catch (e) {
    console.error('  ✗ Failed: ' + e.message);
    if (e.raw && e.raw.message) console.error('    Stripe: ' + e.raw.message);
    process.exit(1);
  }
  console.log('  ✓ Account: ' + acct.id);
  console.log('    Business name: ' + (acct.business_profile?.name || '(not set — fix in Dashboard)'));
  console.log('    Country: ' + acct.country);
  console.log('    Default currency: ' + acct.default_currency);
  console.log('    Charges enabled: ' + acct.charges_enabled);
  console.log('    Payouts enabled: ' + acct.payouts_enabled);
  if (DRY_RUN) console.log('    MODE: DRY RUN — no changes will be made.');

  if (!acct.charges_enabled) {
    console.error('\n  ✗ Account cannot accept live charges yet — finish Dashboard onboarding first.');
    process.exit(1);
  }

  const envOut = [];
  const priceIdByEnvKey = {};
  const productIdByKey = {};

  // ---- 1. Products + Prices ----
  for (const plan of PLANS) {
    header('Product: ' + plan.productKey);

    let product = await findProductByName(stripe, plan.name);
    if (product) {
      console.log('  ✓ Product exists: ' + product.id + ' — ' + product.name);
    } else {
      console.log('  + Creating product: ' + plan.name);
      if (DRY_RUN) {
        product = { id: 'prod_DRYRUN' };
      } else {
        product = await stripe.products.create({
          name: plan.name,
          description: plan.description,
          metadata: plan.metadata,
          tax_code: SAAS_TAX_CODE,
        });
        console.log('    Created: ' + product.id);
      }
    }
    productIdByKey[plan.productKey] = product.id;

    for (const ps of plan.prices) {
      let price = await findPriceByLookupKey(stripe, ps.lookupKey);
      if (price) {
        const interval = price.recurring?.interval || 'one-time';
        console.log('    ✓ Price exists: ' + price.id + ' (' + fmtUSD(price.unit_amount) + ' ' + interval + ')');
      } else {
        const params = {
          product: product.id,
          currency: ps.currency,
          unit_amount: ps.amount,
          lookup_key: ps.lookupKey,
          nickname: ps.nickname,
          metadata: { plan: plan.productKey, interval: ps.interval || 'one_time' },
          transfer_lookup_key: true,
        };
        if (!ps.oneTime) {
          params.recurring = { interval: ps.interval, interval_count: 1, usage_type: 'licensed' };
        }
        console.log('    + Creating price: ' + fmtUSD(ps.amount) + ' ' + (ps.interval || 'one-time') + ' — ' + ps.lookupKey);
        if (DRY_RUN) {
          price = { id: 'price_DRYRUN' };
        } else {
          price = await stripe.prices.create(params);
          console.log('      Created: ' + price.id);
        }
      }
      priceIdByEnvKey[ps.envKey] = price.id;
      envOut.push(ps.envKey + '=' + price.id);
    }
  }

  // ---- 2. Webhook endpoint ----
  header('Webhook endpoint: ' + WEBHOOK_URL);
  let webhook = await findWebhookByUrl(stripe, WEBHOOK_URL);
  let webhookSecret = null;

  if (webhook) {
    console.log('  ✓ Webhook exists: ' + webhook.id);
    console.log('    Status: ' + webhook.status);
    console.log('    Events: ' + webhook.enabled_events.join(', '));
    // Retrieve with secret included
    if (!DRY_RUN) {
      try {
        const full = await stripe.webhookEndpoints.retrieve(webhook.id);
        webhookSecret = full.secret || null;
      } catch (_) { /* restricted keys may not return secret on retrieve */ }
    }
    if (!webhookSecret) {
      console.log('    ⚠ Existing endpoint — signing secret not returned.');
      console.log('      Options:');
      console.log('        (a) Open the endpoint in Stripe Dashboard → "Reveal" signing secret → copy whsec_…');
      console.log('        (b) Re-run this script with --rotate-webhook to mint a fresh secret');
      console.log('        (c) Rotate via API:  stripe webhook_endpoints update ' + webhook.id + ' --roll-secret');
    }
  } else {
    console.log('  + Creating webhook endpoint');
    console.log('    Events: ' + WEBHOOK_EVENTS.join(', '));
    if (DRY_RUN) {
      webhook = { id: 'we_DRYRUN' };
    } else {
      webhook = await stripe.webhookEndpoints.create({
        url: WEBHOOK_URL,
        enabled_events: WEBHOOK_EVENTS,
        description: 'NCII Shield — production webhook',
        metadata: { app: 'ncii-shield', env: 'production' },
      });
      webhookSecret = webhook.secret;  // ONLY returned at creation time
      console.log('    Created: ' + webhook.id);
    }
  }

  if (webhookSecret) {
    envOut.push('STRIPE_WEBHOOK_SECRET=' + webhookSecret);
  }

  // ---- 3. Round-trip verification ----
  if (!DRY_RUN) {
    header('Round-trip verification');
    for (const ps of PLANS.flatMap(p => p.prices)) {
      const found = await findPriceByLookupKey(stripe, ps.lookupKey);
      if (!found) {
        console.error('  ✗ MISSING after create: ' + ps.lookupKey);
        process.exit(1);
      }
      console.log('  ✓ ' + ps.lookupKey + ' → ' + found.id);
    }
    console.log('  ✓ All ' + PLANS.flatMap(p => p.prices).length + ' prices retrievable');
  }

  // ---- 4. Output .env fragment ----
  header('Output — paste into .env (local) and cPanel env editor (production)');
  console.log('\n# Auto-generated by setup-stripe-products.js on ' + new Date().toISOString());
  console.log('STRIPE_SECRET_KEY=' + (DRY_RUN ? '<your-key>' : '<paste-the-live-key-here>'));
  console.log('STRIPE_PUBLISHABLE_KEY=' + (process.env.STRIPE_PUBLISHABLE_KEY || '<your-pk_live_…>'));
  for (const line of envOut) console.log(line);

  if (!DRY_RUN) {
    header('product/price summary');
    console.log('\n  product_id_by_key:');
    for (const [k, v] of Object.entries(productIdByKey)) console.log('    ' + k.padEnd(20) + ' = ' + v);
    console.log('\n  price_id_by_env_key:');
    for (const [k, v] of Object.entries(priceIdByEnvKey)) console.log('    ' + k.padEnd(34) + ' = ' + v);
    if (webhookSecret) {
      console.log('\n  webhook_secret: ' + webhookSecret.slice(0, 12) + '…' + webhookSecret.slice(-4) + ' (len=' + webhookSecret.length + ')');
    }
  }

  header('Manual steps remaining');
  console.log(`
  1. In Stripe Dashboard:
       Settings → Tax → Enable                    (so checkout's automatic_tax computes)
       Settings → Customer portal → Allow promotion codes
       Settings → Branding → set business name, logo, support email
       Settings → Public details → set support email, country, currency

  2. (Optional) Smoke-test webhook delivery locally:
       stripe listen --forward-to https://takedowns.vesamuni.com/webhook/stripe
       stripe trigger checkout.session.completed

  3. Verify the app boots with these values — restart Node.js App in cPanel,
     hit https://takedowns.vesamuni.com/health/ready
     Should return 200 { status: "ok" } once DB + workers are up.

  4. Make a real $3 credit-pack purchase from /billing in a browser,
     confirm the email lands + the takedown_worker records the grant.

  5. After launch + you confirm everything works → ROLL THIS KEY in
     Stripe Dashboard → API keys → click the restricted key → Roll.
     Then update STRIPE_SECRET_KEY on the server with the new value.
`);
}

main().catch(e => {
  console.error('\n✗ Setup failed:', e.message);
  if (e.raw) {
    console.error('  Stripe error code: ' + e.raw.code);
    console.error('  Stripe message:    ' + e.raw.message);
  }
  process.exit(1);
});