/**
 * Script de support : synchronise l'abonnement d'un utilisateur depuis
 * Stripe vers la base de données locale.
 *
 * À utiliser quand un webhook a été raté (par exemple `stripe listen`
 * n'était pas démarré pendant le checkout) et qu'un user payant est
 * resté bloqué en plan "gratuit" dans la DB.
 *
 * Usage :
 *   npm run stripe:sync -- user@example.com
 *   npm run stripe:sync -- --all        # synchronise tous les users avec stripeId
 */

import 'dotenv/config';
import Stripe from 'stripe';
import { PrismaClient } from '@prisma/client';

const GREEN = '\x1b[32m';
const RED = '\x1b[31m';
const YELLOW = '\x1b[33m';
const DIM = '\x1b[2m';
const BOLD = '\x1b[1m';
const RESET = '\x1b[0m';

function log(msg: string) {
  // eslint-disable-next-line no-console
  console.log(msg);
}

interface PlanMapping {
  priceId: string | undefined;
  plan: 'essential' | 'premium';
}

function getPlans(): PlanMapping[] {
  return [
    { priceId: process.env.STRIPE_PRICE_ID_ESSENTIAL, plan: 'essential' },
    { priceId: process.env.STRIPE_PRICE_ID_PREMIUM, plan: 'premium' },
  ];
}

async function syncUser(
  stripe: Stripe,
  prisma: PrismaClient,
  user: { id: string; email: string; stripeId: string | null }
): Promise<{ plan: string; status: string | null } | null> {
  if (!user.stripeId) {
    log(`  ${YELLOW}↷${RESET} ${user.email} ${DIM}(pas de stripeId)${RESET}`);
    return null;
  }

  const subscriptions = await stripe.subscriptions.list({
    customer: user.stripeId,
    status: 'all',
    limit: 5,
  });

  const active = subscriptions.data
    .filter((s) => ['active', 'trialing', 'past_due'].includes(s.status))
    .sort((a, b) => b.created - a.created)[0];

  if (!active) {
    await prisma.user.update({
      where: { id: user.id },
      data: {
        subscription: 'free',
        subscriptionStatus: null,
        stripeSubscriptionId: null,
        subscriptionPriceId: null,
        subscriptionCurrentPeriodEnd: null,
      },
    });
    log(`  ${DIM}−${RESET} ${user.email} ${DIM}→ free (aucun abo actif sur Stripe)${RESET}`);
    return { plan: 'free', status: null };
  }

  const priceId = active.items.data[0]?.price.id;
  const mapping = getPlans().find((p) => p.priceId === priceId);
  const planId = mapping?.plan ?? 'essential';

  await prisma.user.update({
    where: { id: user.id },
    data: {
      subscription: planId,
      subscriptionStatus: active.status,
      stripeSubscriptionId: active.id,
      subscriptionPriceId: priceId ?? null,
      subscriptionCurrentPeriodEnd: new Date(active.current_period_end * 1000),
    },
  });

  log(
    `  ${GREEN}✓${RESET} ${user.email} ${DIM}→${RESET} ${BOLD}${planId}${RESET} ${DIM}(${active.status})${RESET}`
  );
  return { plan: planId, status: active.status };
}

async function main() {
  const args = process.argv.slice(2).filter((a) => a !== '--');

  if (args.length === 0) {
    log('Usage : npm run stripe:sync -- user@example.com');
    log('        npm run stripe:sync -- --all');
    process.exit(1);
  }

  const secretKey = process.env.STRIPE_SECRET_KEY;
  if (!secretKey) {
    log(`${RED}✗ STRIPE_SECRET_KEY manquante${RESET}`);
    process.exit(1);
  }

  const stripe = new Stripe(secretKey, {
    apiVersion:
      (process.env.STRIPE_API_VERSION as Stripe.StripeConfig['apiVersion']) ?? '2023-10-16',
  });
  const prisma = new PrismaClient();

  log('');
  log(`${BOLD}🔄 Synchronisation Stripe → DB${RESET}`);
  log('');

  try {
    if (args.includes('--all')) {
      const users = await prisma.user.findMany({
        where: { stripeId: { not: null } },
        select: { id: true, email: true, stripeId: true },
      });
      log(`${DIM}${users.length} utilisateur(s) avec un stripeId${RESET}`);
      log('');
      for (const user of users) {
        try {
          await syncUser(stripe, prisma, user);
        } catch (err) {
          log(`  ${RED}✗${RESET} ${user.email} : ${(err as Error).message}`);
        }
      }
    } else {
      const email = args[0];
      const user = await prisma.user.findUnique({
        where: { email },
        select: { id: true, email: true, stripeId: true },
      });
      if (!user) {
        log(`${RED}✗ Utilisateur introuvable : ${email}${RESET}`);
        process.exit(1);
      }
      await syncUser(stripe, prisma, user);
    }
    log('');
    log(`${GREEN}✓ Terminé${RESET}`);
  } catch (err) {
    log(`${RED}✗ ${(err as Error).message}${RESET}`);
    process.exit(1);
  } finally {
    await prisma.$disconnect();
  }
}

main();
