/**
 * `kasy new` — unified command to create a complete Flutter project.
 *
 * Flow:
 *   1. Language — use stored preference or ask + save
 *   2. Environment checks (non-blocking warnings)
 *   3. Backend selection (Firebase / Supabase / API)
 *   4. Backend tool checks (non-blocking warnings)
 *   5. Collect answers (name, bundle ID, credentials, modules)
 *   6. Run generator (copy → pub get → build_runner → flutterfire → deploy)
 *   7. Show results + next steps
 */

const path = require('node:path');
const crypto = require('node:crypto');
const kleur = require('kleur');

function generateWebhookKey() {
  return 'rc_wh_' + crypto.randomBytes(16).toString('hex');
}

function openUrl(url) {
  try {
    const { exec } = require('node:child_process');
    const cmd = process.platform === 'darwin' ? `open "${url}"`
      : process.platform === 'win32' ? `start "" "${url}"`
      : `xdg-open "${url}"`;
    exec(cmd, { shell: true });
  } catch (_) {}
}
const ui = require('../utils/ui');
const { printBanner, infoBox, successBox } = require('../utils/brand');
const fs = require('fs-extra');
const { createTranslator } = require('../utils/i18n');
const { getStoredLanguage, setStoredLanguage } = require('../utils/license');
const { promptLanguage } = require('../utils/prompts');
const { warnIfOutdatedBeforeNew } = require('../utils/updates');
const {
  getBaseChecks,
  getPlatformChecks,
  getBackendChecks,
  runChecks,
  hasRequiredFailures,
} = require('../utils/checks');
const { normalizeBackend, getVisibleFeatures } = require('../scaffold/catalog');

// Audience gate: set KASY_INTERNAL=1 to reveal beta/internal features.
const KASY_AUDIENCE = process.env.KASY_INTERNAL === '1' ? 'internal' : 'public';
const { generateFirebaseProject } = require('../scaffold/backends/firebase/generator');
const { generateSupabaseProject } = require('../scaffold/backends/supabase/generator');
const { generateApiProject } = require('../scaffold/backends/api/generator');
const { createProjectAndGetKeys, setupLinkedProject, checkLoggedIn, getOrgsList, getProjectsByOrg, getProjectKeys } = require('../scaffold/backends/supabase/deploy');
const { writeSupabaseGoogleAuthOptions, readSupabaseGoogleCredentials, getGoogleClientSecretViaGcloud } = require('../scaffold/shared/post-build');
const { toPackageName } = require('../scaffold/backends/firebase/tokens');
const { setupFromScratch, setupExistingProject, listBillingAccounts, listGcpOrganizations, checkGcloudAuth, getGcloudInstallInstructions, enableAuthProviders, registerDebugSha1 } = require('../scaffold/backends/firebase/setup-from-scratch');
const { createFcmServiceAccountKey } = require('../scaffold/shared/fcm-service-account');

// Região padrão para criação de projetos Supabase via API.
// Supabase regions: https://supabase.com/docs/guides/platform/regions
const DEFAULT_SUPABASE_REGION = 'sa-east-1'; // São Paulo

const BILLING_OTHER = '__other__';

/**
 * Prompts user to select a billing account. Always shows when 1+ accounts exist.
 * Includes "Other - enter ID manually" so user can use another account (e.g. when default has quota exceeded).
 * @param {Function} tr - Translator
 * @param {Function} onCancel - Cancel handler
 * @returns {Promise<string|null>} Selected billing account ID, or null to use default (first)
 */
async function promptBillingAccountIfNeeded(tr, onCancel) {
  const billingList = await listBillingAccounts();
  if (!billingList.ok) return null;

  const askManualId = async () => {
    const manualId = await ui.text({
      message: tr('new.firebase.q.billingAccount.manualId'),
      placeholder: tr('new.firebase.q.billingAccount.manualId.hint'),
      validate: (v) => (v && v.trim() ? undefined : tr('new.firebase.q.billingAccount.manualId.required')),
      onCancel,
    });
    return manualId?.trim() || null;
  };

  if (!billingList.accounts?.length) {
    ui.note(tr('new.firebase.q.billingAccount.context'));
    return askManualId();
  }

  ui.note(tr('new.firebase.q.billingAccount.context'));
  const billingAccountId = await ui.select({
    message: tr('new.firebase.q.billingAccount'),
    initialValue: billingList.accounts[0].id,
    options: [
      ...billingList.accounts.map((a) => ({
        value: a.id,
        label: `${a.name || a.id} (${a.id})`,
      })),
      { value: BILLING_OTHER, label: tr('new.firebase.q.billingAccount.other') },
    ],
    onCancel,
  });

  if (billingAccountId === BILLING_OTHER) return askManualId();
  return billingAccountId;
}

/**
 * Prompts user to select a GCP organization. Only shows when the account has ≥1 organization.
 * Personal accounts (no orgs) silently skip this prompt.
 * @param {Function} tr - Translator
 * @param {Function} onCancel - Cancel handler
 * @returns {Promise<string|null>} Selected organization ID, or null (no organization / personal account)
 */
async function promptOrganizationIfNeeded(tr, onCancel) {
  const orgList = await listGcpOrganizations();
  if (!orgList.ok || !orgList.organizations?.length) return null;
  const organizationId = await ui.select({
    message: tr('new.firebase.q.organization'),
    initialValue: orgList.organizations[0].id,
    options: [
      ...orgList.organizations.map((o) => ({
        value: o.id,
        label: `${o.name} (${o.id})`,
      })),
      { value: null, label: tr('new.firebase.q.organization.none') },
    ],
    onCancel,
  });
  return organizationId || null;
}

// ── Helpers ───────────────────────────────────────────────────────────────────

function printPrerequisites(tr, backend, firebaseSetupMode = 'existing', checkResults = []) {
  const gcloudOk = checkResults.every(
    (r) => !r.name?.includes('gcloud') || r.ok
  );
  const firebaseCreate = firebaseSetupMode === 'create';
  const items = [];

  if (backend === 'firebase') {
    if (firebaseCreate) {
      if (!gcloudOk) items.push(tr('new.firebase.prereq.create.1'));
      items.push(tr('new.firebase.prereq.create.2'));
      items.push(tr('new.firebase.prereq.create.projectQuota'));
      items.push(tr('new.firebase.prereq.create.note'));
    } else {
      items.push(tr('new.firebase.prereq.1'));
      items.push(tr('new.firebase.prereq.2'));
      items.push(tr('new.firebase.prereq.3'));
      items.push(tr('new.firebase.prereq.4'));
      items.push(tr('new.firebase.prereq.5'));
      items.push(tr('new.firebase.prereq.doc'));
    }
  } else if (backend === 'supabase') {
    if (firebaseCreate) {
      if (!gcloudOk) items.push(tr('new.firebase.prereq.create.1'));
      items.push(tr('new.firebase.prereq.create.2'));
      items.push(tr('new.firebase.prereq.create.projectQuota'));
      items.push(tr('new.firebase.prereq.create.pushNote'));
      items.push(tr('new.supabase.prereq.1'));
      items.push(tr('new.supabase.prereq.2'));
      items.push(tr('new.supabase.prereq.login'));
    } else {
      items.push(tr('new.supabase.prereq.1'));
      items.push(tr('new.supabase.prereq.2'));
      items.push(tr('new.supabase.prereq.3'));
      items.push(tr('new.supabase.prereq.login'));
    }
  } else if (backend === 'api') {
    if (firebaseCreate) {
      if (!gcloudOk) items.push(tr('new.firebase.prereq.create.1'));
      items.push(tr('new.firebase.prereq.create.2'));
      items.push(tr('new.firebase.prereq.create.projectQuota'));
      items.push(tr('new.firebase.prereq.create.pushNote'));
      items.push(tr('new.api.prereq.1'));
      items.push(tr('new.api.prereq.2'));
    } else {
      items.push(tr('new.api.prereq.1'));
      items.push(tr('new.api.prereq.2'));
      items.push(tr('new.api.prereq.3'));
    }
  }

  if (items.length > 0) {
    ui.note(items.map((i) => `• ${i}`).join('\n'), tr('new.prereq.title'));
  }
}

function printSummary(tr, answers) {
  const modules = answers.modules.length > 0
    ? answers.modules.join(', ')
    : tr('new.firebase.confirm.none');

  const backendLabel = { firebase: '🔥 Firebase', supabase: '🟢 Supabase', api: '🔗 API REST' }[answers.backend] || answers.backend;

  const rows = [
    `${kleur.dim(tr('new.firebase.confirm.app') + ':')}        ${kleur.white(answers.appName)}`,
    `${kleur.dim('Bundle:')}        ${kleur.white(answers.bundleId)}`,
    `${kleur.dim('Backend:')}       ${kleur.white(backendLabel)}`,
  ];
  if (answers.firebaseProjectId) rows.push(`${kleur.dim('Firebase:')}      ${kleur.white(answers.firebaseProjectId)}`);
  if (answers.supabaseUrl)       rows.push(`${kleur.dim('Supabase URL:')}  ${kleur.white(answers.supabaseUrl)}`);
  if (answers.apiBaseUrl)        rows.push(`${kleur.dim('API URL:')}       ${kleur.white(answers.apiBaseUrl)}`);
  rows.push(`${kleur.dim(tr('new.firebase.confirm.modules') + ':')}     ${kleur.white(modules)}`);

  console.log(infoBox(`📦 ${tr('new.firebase.confirm.title')}`, rows.join('\n')));
}

const STEP_LABELS = {
  'project-setup':  { en: 'Project configured',                     pt: 'Projeto configurado',                   es: 'Proyecto configurado' },
  'pub-get':        { en: 'Packages installed',                      pt: 'Pacotes instalados',                    es: 'Paquetes instalados' },
  'slang':          { en: 'Translations generated',                  pt: 'Traducoes geradas',                     es: 'Traducciones generadas' },
  'build-runner':   { en: 'Code generated (Riverpod / Freezed)',      pt: 'Codigo gerado (Riverpod / Freezed)',     es: 'Codigo generado (Riverpod / Freezed)' },
  'flutterfire':    { en: 'Firebase configured (flutterfire)',        pt: 'Firebase configurado (flutterfire)',    es: 'Firebase configurado (flutterfire)' },
  'Service Account key': { en: 'Service Account key',                pt: 'Chave de servico',                      es: 'Clave de servicio' },
  'firebase_key.json': { en: 'Service account key copied', pt: 'Chave de conta de servico copiada', es: 'Clave de cuenta de servicio copiada' },
  'npm install (functions)': { en: 'Functions dependencies installed', pt: 'Dependencias das Functions instaladas', es: 'Dependencias de Functions instaladas' },
  'firebase deploy (Functions + Firestore rules + Storage)': {
    en: 'Backend deployed (Functions + rules)',
    pt: 'Backend publicado (Functions + regras)',
    es: 'Backend desplegado (Functions + reglas)',
  },
  'firebase-messaging-sw': {
    en: 'Web: firebase-messaging-sw.js configured',
    pt: 'Web: firebase-messaging-sw.js configurado',
    es: 'Web: firebase-messaging-sw.js configurado',
  },
  'supabase init':     { en: 'Supabase init',        pt: 'Supabase init',        es: 'Supabase init' },
  'supabase link':     { en: 'Supabase linked',      pt: 'Supabase vinculado',   es: 'Supabase vinculado' },
  'supabase db push':  { en: 'Database migrated',    pt: 'Banco migrado',        es: 'Base de datos migrada' },
  'anonymous sign-in': { en: 'Anonymous sign-in enabled', pt: 'Login anonimo ativado', es: 'Inicio de sesion anonimo activado' },
  'google sign-in': { en: 'Google Sign-In enabled', pt: 'Google Sign-In ativado', es: 'Google Sign-In activado' },
  'google-auth-options': { en: 'Google client IDs written', pt: 'Client IDs do Google gravados', es: 'Client IDs de Google escritos' },
  'ios-url-scheme': { en: 'iOS URL scheme registered', pt: 'URL scheme iOS registrado', es: 'URL scheme iOS registrado' },
  'google-ios-url-scheme': { en: 'iOS Google URL scheme registered', pt: 'URL scheme Google iOS registrado', es: 'URL scheme Google iOS registrado' },
  'deploy revenuecat-webhook': { en: 'RevenueCat webhook deployed', pt: 'Webhook RevenueCat publicado', es: 'Webhook RevenueCat desplegado' },
  'secret REVENUECAT_WEBHOOK_KEY': { en: 'RevenueCat webhook secret', pt: 'Secret webhook RevenueCat', es: 'Secret webhook RevenueCat' },
  'secret META_ACCESS_TOKEN': { en: 'Meta Access Token', pt: 'Meta Access Token', es: 'Meta Access Token' },
  'secret META_DATASET_ID': { en: 'Meta Dataset ID', pt: 'Meta Dataset ID', es: 'Meta Dataset ID' },
  'fcm-key': { en: 'FCM Service Account key generated', pt: 'Chave FCM gerada automaticamente', es: 'Clave FCM generada automáticamente' },
  'fcm-key-saved': { en: 'FCM key saved to .kasy/', pt: 'Chave FCM salva em .kasy/', es: 'Clave FCM guardada en .kasy/' },
  'secret FIREBASE_SERVICE_ACCOUNT_JSON': { en: 'FCM Service Account configured', pt: 'Service Account FCM configurado', es: 'Service Account FCM configurado' },
  'gcp-project': { en: 'GCP project created', pt: 'Projeto GCP criado', es: 'Proyecto GCP creado' },
  'billing': { en: 'Billing account linked (Blaze)', pt: 'Conta de faturamento vinculada (Blaze)', es: 'Cuenta de facturación vinculada (Blaze)' },
  'add-firebase': { en: 'Firebase added to project', pt: 'Firebase adicionado ao projeto', es: 'Firebase añadido al proyecto' },
  'enable-apis': { en: 'APIs enabled', pt: 'APIs ativadas', es: 'APIs habilitadas' },
  'android-app': { en: 'Android app registered', pt: 'App Android registrado', es: 'App Android registrado' },
  'ios-app': { en: 'iOS app registered', pt: 'App iOS registrado', es: 'App iOS registrado' },
  'web-app': { en: 'Web app registered', pt: 'App Web registrado', es: 'App Web registrado' },
  'sha1': { en: 'SHA-1 added for Google Sign-In', pt: 'SHA-1 adicionado para Google Sign-In', es: 'SHA-1 añadido para Google Sign-In' },
  'service-account': { en: 'Service account key created', pt: 'Chave de conta de servico criada', es: 'Clave de cuenta de servicio creada' },
  'firestore': { en: 'Firestore database created', pt: 'Banco Firestore criado', es: 'Base de datos Firestore creada' },
  'storage': { en: 'Firebase Storage bucket created', pt: 'Bucket Firebase Storage criado', es: 'Bucket Firebase Storage creado' },
};

const STEP_PROGRESS = {
  'project-setup': { en: 'Configuring your project…',               pt: 'Configurando seu projeto…',              es: 'Configurando tu proyecto…' },
  'pub-get':       { en: 'Installing packages… (may take a few minutes)',        pt: 'Instalando pacotes… (pode levar alguns minutos)',        es: 'Instalando paquetes… (puede tardar varios minutos)' },
  'slang':         { en: 'Generating translations…',                             pt: 'Gerando traducoes…',                                     es: 'Generando traducciones…' },
  'build-runner':  { en: 'Generating code (Riverpod / Freezed)… (may take a few minutes)', pt: 'Gerando codigo (Riverpod / Freezed)… (pode demorar)', es: 'Generando codigo (Riverpod / Freezed)… (puede tardar)' },
  'flutterfire':   { en: 'Connecting to Firebase…',                 pt: 'Conectando ao Firebase…',                es: 'Conectando a Firebase…' },
  'deploy':        { en: 'Deploying backend to Firebase…',          pt: 'Publicando backend no Firebase…',        es: 'Desplegando backend en Firebase…' },
  'gcp-project':   { en: 'Creating GCP project…',                   pt: 'Criando projeto GCP…',                    es: 'Creando proyecto GCP…' },
  'billing':       { en: 'Linking billing account (Blaze)…',         pt: 'Vinculando conta de faturamento (Blaze)…', es: 'Vinculando cuenta de facturación (Blaze)…' },
  'add-firebase':  { en: 'Adding Firebase…',                       pt: 'Adicionando Firebase…',                 es: 'Añadiendo Firebase…' },
  'enable-apis':   { en: 'Enabling APIs…',                         pt: 'Ativando APIs…',                          es: 'Habilitando APIs…' },
  'android-app':   { en: 'Registering Android app…',                pt: 'Registrando app Android…',               es: 'Registrando app Android…' },
  'ios-app':       { en: 'Registering iOS app…',                     pt: 'Registrando app iOS…',                    es: 'Registrando app iOS…' },
  'web-app':       { en: 'Registering Web app…',                     pt: 'Registrando app Web…',                    es: 'Registrando app Web…' },
  'sha1':          { en: 'Adding SHA-1 for Google Sign-In…',        pt: 'Adicionando SHA-1 para Google Sign-In…', es: 'Añadiendo SHA-1 para Google Sign-In…' },
  'service-account': { en: 'Creating service account key…',         pt: 'Criando chave de conta de servico…',      es: 'Creando clave de cuenta de servicio…' },
  'firestore':     { en: 'Creating Firestore database…',            pt: 'Criando banco Firestore…',                es: 'Creando base de datos Firestore…' },
  'storage':       { en: 'Creating Firebase Storage bucket…',       pt: 'Criando bucket Firebase Storage…',        es: 'Creando bucket Firebase Storage…' },
  'deploy-retry-wait':   { en: 'Waiting 4 min for GCP permissions to propagate… (do not close terminal)', pt: 'Aguardando 4 min para permissões do GCP propagarem… (não feche o terminal)', es: 'Esperando 4 min para propagar permisos de GCP… (no cierres la terminal)' },
  'deploy-retry-wait-2': { en: 'Retrying with updated permissions, 2 more min… (almost there!)', pt: 'Tentando novamente com permissões atualizadas, mais 2 min… (quase lá!)', es: 'Reintentando con permisos actualizados, 2 min más… (¡casi listo!)' },
};

function stepLabel(key, lang) {
  const map = STEP_LABELS[key];
  return map ? (map[lang] || map.en || key) : key;
}

function stepProgress(key, lang) {
  const map = STEP_PROGRESS[key];
  return map ? (map[lang] || map.en || key) : key;
}

/**
 * Print the post-setup status block for a create-from-scratch result.
 * Shows SHA-1, Firestore, Storage status with URLs for manual action when needed.
 */
function printCreateFromScratchStatus(result, tr) {
  if (result.sha1Skipped) {
    const err = (result.sha1Error || '').replace(/\s+/g, ' ').slice(0, 120);
    const msg = result.sha1Skipped === 'api_failed'
      ? tr('new.sha1.skipped.apiFailed', { error: err })
      : tr('new.sha1.skipped.other', { reason: result.sha1Skipped });
    const url = result.sha1ManualUrl ? `\n${kleur.cyan(result.sha1ManualUrl)}` : '';
    ui.log.warn(`${msg}\n${tr('new.sha1.addManually')}${url}`);
  } else {
    ui.log.success(tr('new.sha1.added'));
  }

  if (result.firestoreCreated) {
    ui.log.success(tr('new.firestore.created'));
  } else {
    const msg = result.firestoreError
      ? tr('new.firestore.notCreated.error', { error: (result.firestoreError || '').slice(0, 100) })
      : tr('new.firestore.notCreated');
    ui.log.warn(`${msg}\n${tr('new.activateManually')}\n${kleur.cyan(result.firestoreUrl)}`);
  }

  if (result.storageCreated) {
    ui.log.success(tr('new.storage.created'));
  } else {
    const msg = result.storageError
      ? tr('new.storage.notCreated.error', { error: (result.storageError || '').slice(0, 100) })
      : tr('new.storage.notCreated');
    ui.log.warn(`${msg}\n${tr('new.activateManually')}\n${kleur.cyan(result.storageUrl)}`);
  }
}

function printSuccessCard(tr, answers, targetDir) {
  const folderName = path.basename(targetDir);
  const consoleUrl = answers.backend === 'firebase' && answers.firebaseProjectId
    ? `https://console.firebase.google.com/project/${answers.firebaseProjectId}`
    : answers.backend === 'supabase'
      ? 'https://supabase.com/dashboard'
      : answers.apiBaseUrl || null;

  const lines = [];
  lines.push(kleur.bold(tr('new.success.nextSteps')));
  lines.push('');
  lines.push(`${kleur.dim('1.')} ${kleur.dim(tr('new.success.step.cd'))}`);
  lines.push(`   ${kleur.cyan(`cd ${folderName}`)}`);
  let stepNum = 2;
  if (answers.backend === 'firebase') {
    lines.push('');
    lines.push(`${kleur.dim(`${stepNum++}.`)} ${kleur.dim(tr('new.success.step.deploy'))}`);
    lines.push(`   ${kleur.cyan('kasy deploy')}`);
  }
  lines.push('');
  lines.push(`${kleur.dim(`${stepNum++}.`)} ${kleur.dim(tr('new.success.step.run'))}`);
  lines.push(`   ${kleur.cyan('kasy run')}`);
  lines.push(`   ${kleur.dim(tr('new.success.step.run.vscode'))}`);
  if (consoleUrl) {
    lines.push('');
    lines.push(`${kleur.dim(`${stepNum}.`)} ${kleur.dim(tr('new.success.step.console'))}`);
    lines.push(`   ${kleur.cyan(consoleUrl)}`);
  }

  console.log(successBox(`🎉 ${tr('new.success.title')}`, lines.join('\n')));
}

function printStepResult(step, lang = 'pt') {
  const label = stepLabel(step.name, lang);
  if (step.skipped) {
    ui.log.step(`${kleur.dim(label)} ${kleur.dim('(skipped)')}`);
    return;
  }
  const detail = step.detail ? kleur.dim(` — ${step.detail.split('\n')[0]}`) : '';
  if (step.ok) {
    ui.log.success(`${label}${detail}`);
  } else {
    ui.log.error(`${label}${detail}`);
  }
}

function onCancel(tr) {
  ui.cancel(tr('new.firebase.error.aborted'));
  process.exit(0);
}

async function promptSupabaseManual(tr, cancel) {
  const supabaseUrl = await ui.text({
    message: tr('prompt.supabase.url.enter'),
    placeholder: 'https://xxxx.supabase.co',
    validate: (v) => (v && v.trim() ? undefined : tr('prompt.supabase.url.required')),
    onCancel: cancel,
  });
  const supabaseAnonKey = await ui.text({
    message: tr('prompt.supabase.anonKey.enter'),
    validate: (v) => (v && v.trim() ? undefined : tr('prompt.supabase.anonKey.required')),
    onCancel: cancel,
  });
  return { supabaseUrl, supabaseAnonKey };
}

// ── Module presets ────────────────────────────────────────────────────────────

function buildPresets(audience) {
  const catalog = getVisibleFeatures({ audience });
  const named = ['starter', 'saas', 'content', 'full'];
  const result = { none: [], custom: null };
  for (const name of named) {
    result[name] = catalog.filter((f) => f.defaultInPresets.includes(name)).map((f) => f.id);
  }
  return result;
}

const MODULE_PRESETS = buildPresets(KASY_AUDIENCE);

// ── Main wizard ───────────────────────────────────────────────────────────────

/**
 * @param {string} directory - target directory (relative or absolute)
 * @param {object} opts
 * @param {string} [opts.language]     - language hint ('pt' | 'en' | 'es'). Uses stored preference if omitted.
 * @param {string} [opts.backend]      - backend hint ('firebase' | 'supabase' | 'api'). Skips selection prompt if valid.
 * @param {string} [opts.withModules]  - comma-separated modules to pre-select (e.g. 'revenuecat,sentry,ci').
 */
async function runNew(directory, { language: langHint = null, backend: backendHint = null, withModules = null, yes = false, project: projectHint = null } = {}) {
  // ── 1. Language — use stored preference, or ask once and save ───────────
  const storedLang = getStoredLanguage();
  let language = storedLang;
  if (!language) {
    language = await promptLanguage(langHint);
    setStoredLanguage(language);
  }

  const tr = createTranslator(language);
  const cancel = () => onCancel(tr);

  // ── 1b. Version check — warn if outdated, let user decide ───────────────
  if (!yes) {
    await warnIfOutdatedBeforeNew(tr);
  }

  // ── 2. Environment checks (non-blocking — only warnings) ────────────────
  const envChecks = [...getBaseChecks(), ...getPlatformChecks()];
  await runChecks(envChecks, tr('new.checks.environment'), {
    t: tr,
    compact: true,
    spinnerLabel: tr('new.checks.environment.checking'),
    doneLabel: tr('new.checks.environment.done'),
  });

  printBanner(tr);

  ui.intro(kleur.bold(tr('new.subtitle2')));

  // Whether an explicit target directory was provided by the user
  const hasExplicitDir = directory && directory !== '.';

  // If no directory was given, we'll determine targetDir AFTER asking the app name
  let targetDir = hasExplicitDir
    ? path.resolve(process.cwd(), directory)
    : null;

  // Guard for explicit dir: must be empty or not exist
  if (targetDir && await fs.pathExists(targetDir)) {
    const contents = await fs.readdir(targetDir);
    if (contents.length > 0) {
      throw new Error(tr('new.firebase.error.dirNotEmpty', { path: targetDir }));
    }
  }

  // ── 3. Backend selection (support pre-selection via --backend flag) ─────
  let backend = normalizeBackend(backendHint);
  if (!backend) {
    backend = await ui.select({
      message: tr('new.q.backend'),
      initialValue: 'firebase',
      options: [
        { value: 'firebase', label: '🔥 Firebase', hint: tr('new.q.backend.firebase.desc') },
        { value: 'supabase', label: '🟢 Supabase', hint: tr('new.q.backend.supabase.desc') },
        { value: 'api',      label: '🔗 API REST', hint: tr('new.q.backend.api.desc') },
      ],
      onCancel: cancel,
    });
  } else {
    const backendLabel = { firebase: '🔥 Firebase', supabase: '🟢 Supabase', api: '🔗 API REST' }[backend] || backend;
    ui.log.info(`Backend: ${kleur.white(backendLabel)}`);
  }

  // ── 3b. Backend tool checks (required — blocks if missing) ───────────────
  const backendCheckKey = backend === 'api' ? 'restApi' : backend;
  const backendChecks = getBackendChecks(backendCheckKey);
  let backendCheckResults = [];
  if (backendChecks.length > 0) {
    if (backend === 'supabase' || backend === 'api') {
      ui.log.info(tr('new.checks.firebaseForPush'));
    }
    const backendLabel = tr('setup.checks.backend', { backend }) || `  Checking ${backend} tools…`;
    backendCheckResults = await runChecks(backendChecks, backendLabel, {
      t: tr,
      compact: true,
      spinnerLabel: tr('setup.checks.backend.checking', { backend }) || `Checking ${backend} tools`,
      doneLabel: tr('setup.checks.backend.done', { backend }) || `${backend} tools ready`,
    });
    if (hasRequiredFailures(backendCheckResults)) {
      const installSteps = [tr('new.checks.installFirebase')];
      if (backend === 'supabase') {
        const platform = process.platform === 'darwin' ? 'darwin' : process.platform === 'win32' ? 'win32' : 'linux';
        const supabaseKey = `new.checks.installSupabase.${platform}`;
        installSteps.push(tr(supabaseKey) || tr('new.checks.installSupabase'));
      }
      ui.log.error(tr('new.checks.requiredBlock'));
      ui.note(installSteps.map((s) => `• ${s}`).join('\n'));
      ui.cancel(tr('new.firebase.error.aborted'));
      process.exit(1);
    }
  }

  // ── App identity: name + bundle ID — first things first ────────────────────
  let core;
  if (yes) {
    if (!hasExplicitDir) {
      ui.log.error('--yes requires an app name: kasy new MyApp --yes');
      ui.cancel(tr('new.firebase.error.aborted'));
      process.exit(1);
    }
    const appName = path.basename(targetDir);
    const slug = appName
      .normalize('NFD')
      .replace(/[̀-ͯ]/g, '')
      .toLowerCase()
      .replace(/[^a-z0-9]/g, '');
    const bundleId = (slug && !/^\d/.test(slug)) ? `com.${slug}.app` : 'com.example.app';
    core = { appName, bundleId };
    ui.log.info(`App: ${kleur.white(appName)}`);
    ui.log.info(`Bundle: ${kleur.white(bundleId)}`);
  } else {
    const appName = await ui.text({
      message: tr('new.firebase.q.appName'),
      placeholder: tr('new.firebase.q.appName.hint'),
      initialValue: hasExplicitDir ? path.basename(targetDir) : '',
      validate: (v) => (v && v.trim() ? undefined : tr('new.firebase.q.appName.required')),
      onCancel: cancel,
    });

    const defaultBundleId = (() => {
      const name = (appName || '').trim();
      if (!name) return 'com.example.app';
      const slug = name
        .normalize('NFD')
        .replace(/[̀-ͯ]/g, '')
        .toLowerCase()
        .replace(/[^a-z0-9]/g, '');
      if (!slug || /^\d/.test(slug)) return 'com.example.app';
      return `com.${slug}.app`;
    })();

    const bundleId = await ui.text({
      message: tr('new.firebase.q.bundleId'),
      placeholder: tr('new.firebase.q.bundleId.hint'),
      initialValue: defaultBundleId,
      validate: (v) => {
        if (!v || !v.trim()) return tr('new.firebase.q.bundleId.required');
        return /^[a-zA-Z][\w]*(\.[a-zA-Z][\w]*)+$/.test(v.trim())
          ? undefined
          : tr('new.firebase.q.bundleId.invalid');
      },
      onCancel: cancel,
    });

    core = { appName, bundleId };
  }

  // Resolve targetDir now that we have the app name
  if (!targetDir) {
    const folderName = toPackageName(core.appName.trim());
    targetDir = path.resolve(process.cwd(), folderName);
    if (await fs.pathExists(targetDir)) {
      const contents = await fs.readdir(targetDir);
      if (contents.length > 0) {
        throw new Error(tr('new.firebase.error.dirNotEmpty', { path: targetDir }));
      }
    }
  }

  // ── Firebase setup mode (create vs existing) ────────────────────────────────
  // Firebase backend: full setup. Supabase/API: Firebase only for push notifications (FCM).
  let firebaseSetupMode = 'existing';
  if (!yes) {
    if (backend === 'firebase') {
      firebaseSetupMode = await ui.select({
        message: tr('new.firebase.q.setupMode'),
        initialValue: 'create',
        options: [
          { value: 'create',   label: tr('new.firebase.q.setupMode.create') },
          { value: 'existing', label: tr('new.firebase.q.setupMode.existing') },
        ],
        onCancel: cancel,
      });
    } else if (backend === 'supabase' || backend === 'api') {
      ui.note(tr('new.firebase.q.setupMode.push.explain'));
      firebaseSetupMode = await ui.select({
        message: tr('new.firebase.q.setupMode.push'),
        initialValue: 'create',
        options: [
          { value: 'create',   label: tr('new.firebase.q.setupMode.create') },
          { value: 'existing', label: tr('new.firebase.q.setupMode.existing') },
        ],
        onCancel: cancel,
      });
    }
  }

  // ── Wizard mode — Quick (few questions) or Full (all options) ───────────────
  // Asked after app name + setup context are clear, so the user understands what it controls.
  let isQuick = yes; // --yes implies Quick mode
  if (!yes) {
    const wizardMode = await ui.select({
      message: tr('new.q.mode'),
      initialValue: 'quick',
      options: [
        { value: 'quick',    label: tr('new.q.mode.quick') },
        { value: 'advanced', label: tr('new.q.mode.advanced') },
      ],
      onCancel: cancel,
    });
    isQuick = wizardMode === 'quick';
  }

  // ── Backend-specific prerequisites (dynamic: only show what's not yet verified) ─
  printPrerequisites(tr, backend, firebaseSetupMode, backendCheckResults);

  // ── Firebase project ID (if using an existing project) ──────────────────────
  if (!yes) {
    const needFirebaseProjectId =
      (backend === 'firebase' && firebaseSetupMode === 'existing') ||
      ((backend === 'supabase' || backend === 'api') && firebaseSetupMode === 'existing');
    if (needFirebaseProjectId) {
      const firebaseProjectId = await ui.text({
        message: tr('new.firebase.q.projectId'),
        placeholder: tr('new.firebase.q.projectId.hint') + (backend !== 'firebase' ? ' (FCM + Remote Config)' : ''),
        validate: (v) => {
          if (!v || !v.trim()) return tr('new.firebase.q.projectId.required');
          const id = v.trim();
          if (id.length < 6 || id.length > 30) return 'ID deve ter entre 6 e 30 caracteres (ex: meu-app-123)';
          if (!/^[a-z][a-z0-9-]*[a-z0-9]$/.test(id)) return 'ID inválido: use letras minúsculas, números e hífens, começando com letra (ex: meu-app-123)';
          return undefined;
        },
        onCancel: cancel,
      });
      core.firebaseProjectId = firebaseProjectId;
    }
  } else {
    let firebaseProjectId = projectHint?.trim() || '';
    if (!firebaseProjectId && backend === 'firebase') {
      const pid = await ui.text({
        message: tr('new.firebase.q.projectId'),
        placeholder: tr('new.firebase.q.projectId.hint'),
        validate: (v) => (v && v.trim() ? undefined : tr('new.firebase.q.projectId.required')),
        onCancel: cancel,
      });
      firebaseProjectId = pid?.trim() || '';
    }
    if (firebaseProjectId) {
      core.firebaseProjectId = firebaseProjectId;
      ui.log.info(`Project: ${kleur.white(firebaseProjectId)}`);
    }
  }

  // ── Firebase region — Quick mode uses default (us-central1) ──────────
  let firebaseRegion = 'us-central1';
  if (backend === 'firebase' && !isQuick) {
    const region = await ui.select({
      message: tr('new.firebase.q.region'),
      initialValue: 'us-central1',
      options: [
        { value: 'us-central1',         label: tr('new.firebase.q.region.us') },
        { value: 'europe-west1',        label: tr('new.firebase.q.region.europe') },
        { value: 'southamerica-east1',  label: tr('new.firebase.q.region.brazil') },
      ],
      onCancel: cancel,
    });
    firebaseRegion = region || 'us-central1';
  }

  // ── Firebase: create from scratch (when selected) ─────────────────────────
  let firebaseIncludeWeb = true;
  if (backend === 'firebase' && firebaseSetupMode === 'create') {
    firebaseIncludeWeb = await ui.confirm({
      message: tr('new.firebase.create.includeWeb'),
      initialValue: true,
      onCancel: cancel,
    });
    const gcloudCheck = await checkGcloudAuth();
    if (!gcloudCheck.ok) {
      ui.log.error(tr('new.firebase.create.gcloudRequired'));
      if (gcloudCheck.missing === 'gcloud') {
        const instructions = getGcloudInstallInstructions();
        const noteLines = [tr('new.firebase.create.installTitle')];
        if (instructions.install) noteLines.push(`${tr('new.firebase.create.installCommand')}:\n  ${kleur.cyan(instructions.install)}`);
        if (instructions.hint) noteLines.push(instructions.hint);
        noteLines.push(`${tr('new.firebase.create.installAfter')}:\n  ${kleur.cyan(instructions.after)}`);
        noteLines.push(`${tr('new.firebase.create.installUrl')}: ${instructions.url}`);
        ui.note(noteLines.join('\n\n'));
      } else {
        ui.note(tr('new.firebase.create.authCommand'));
      }
      ui.log.warn(tr('new.firebase.create.fallbackHint'));
      core.firebaseProjectId = await ui.text({
        message: tr('new.firebase.q.projectId'),
        placeholder: tr('new.firebase.q.projectId.hint'),
        validate: (v) => (v && v.trim() ? undefined : tr('new.firebase.q.projectId.required')),
        onCancel: cancel,
      });
    } else {
      const selectedOrgId = await promptOrganizationIfNeeded(tr, cancel);
      let selectedBillingId = await promptBillingAccountIfNeeded(tr, cancel);
      ui.log.info(`${tr('new.firebase.create.estimatedTime')}\n${tr('new.internet.warning')}`);
      const ps1 = ui.makeTimedStepper();
      ps1.next(tr('new.firebase.create.creating'));
      const setupResult = await setupFromScratch(core.appName.trim(), core.bundleId.trim(), {
        includeWeb: firebaseIncludeWeb,
        region: firebaseRegion,
        tr,
        billingAccountId: selectedBillingId || undefined,
        organizationId: selectedOrgId || undefined,
        onProgress: (key, data) => {
          if (key === 'wait-propagate') {
            ps1.next(tr('new.firebase.create.waitPropagate'));
          } else if (key === 'firestore') {
            ps1.next(stepProgress('firestore', language));
          } else if (key === 'storage') {
            ps1.next(stepProgress('storage', language));
          } else if (key === 'auth-providers-warn') {
            ps1.stop();
            ui.log.warn(`${tr('new.firebase.interactive.authWarn')}\n${kleur.cyan(data?.url || '')}`);
          }
        },
      });
      const askReady = async (readyKey) => {
        const ok = await ui.confirm({
          message: tr(readyKey),
          initialValue: true,
          onCancel: cancel,
        });
        if (!ok) {
          ui.cancel(tr('prompt.cancelled'));
          process.exit(0);
        }
      };
      const showBeforeContinue = (step1Key, authUrl) => {
        ui.note(
          `${tr(step1Key)}\n${kleur.cyan(authUrl)}`,
          tr('new.firebase.create.beforeContinue.title')
        );
      };
      if (setupResult.ok) {
        ps1.succeed(tr('new.firebase.create.success'));
        core.firebaseProjectId = setupResult.projectId;
        ui.log.info(`Project ID: ${core.firebaseProjectId}`);
        printCreateFromScratchStatus(setupResult, tr);
        const authUrl = `https://console.firebase.google.com/project/${core.firebaseProjectId}/authentication/providers`;
        if (setupResult.authEnabled && !setupResult.googleSignInSkipped) {
          // Email/Password, Anonymous AND Google Sign-In were all enabled automatically — skip prompt.
        } else {
          // At least one provider needs manual activation.
          const step1Key = setupResult.googleSignInSkipped
            ? 'new.firebase.create.beforeContinue.step1'
            : 'new.firebase.create.beforeContinue.step1.noAuth';
          const readyKey = setupResult.googleSignInSkipped
            ? 'new.firebase.create.beforeContinue.ready'
            : 'new.firebase.create.beforeContinue.ready.noAuth';
          showBeforeContinue(step1Key, authUrl);
          openUrl(authUrl);
          await askReady(readyKey);
        }

      } else {
        ps1.fail(tr('new.firebase.create.failed'));
        let lastResult = setupResult;
        while (lastResult.billingFailed && lastResult.projectId) {
          ui.log.error(`${tr('new.firebase.create.failed')}: ${lastResult.error}`);
          ui.note(
            `${kleur.cyan(lastResult.billingManualLink)}\n\n${tr('new.firebase.create.billingRetry.hint')}`,
            tr('new.firebase.create.billingRetry.title')
          );
          const retry = await ui.confirm({
            message: tr('new.firebase.create.billingRetry.ready'),
            initialValue: true,
            onCancel: cancel,
          });
          if (!retry) {
            ui.log.message(tr('new.firebase.create.billingRetry.exit'));
            ui.log.info(tr('new.firebase.create.useExistingHint', { id: lastResult.projectId }));
            process.exit(0);
          }
          ui.log.message(tr('new.firebase.create.billingRetry.retrying'));
          selectedBillingId = await promptBillingAccountIfNeeded(tr, cancel);
          const ps2 = ui.makeTimedStepper();
          ps2.next(tr('new.firebase.create.creating'));
          lastResult = await setupFromScratch(core.appName.trim(), core.bundleId.trim(), {
                includeWeb: firebaseIncludeWeb,
            region: firebaseRegion,
            tr,
            resumeFromBilling: { projectId: lastResult.projectId },
            billingAccountId: selectedBillingId || undefined,
            organizationId: selectedOrgId || undefined,
            onProgress: (key) => {
              if (key === 'wait-propagate') {
                ps2.next(tr('new.firebase.create.waitPropagate'));
              } else if (key === 'firestore') {
                ps2.next(stepProgress('firestore', language));
              } else if (key === 'storage') {
                ps2.next(stepProgress('storage', language));
              }
            },
          });
          if (lastResult.ok) {
            ps2.succeed(tr('new.firebase.create.success'));
            core.firebaseProjectId = lastResult.projectId;
            ui.log.info(`Project ID: ${core.firebaseProjectId}`);
            printCreateFromScratchStatus(lastResult, tr);
            const authUrl = `https://console.firebase.google.com/project/${core.firebaseProjectId}/authentication/providers`;
            if (lastResult.authEnabled && !lastResult.googleSignInSkipped) {
              // All three providers enabled automatically — skip prompt.
            } else {
              const step1Key = lastResult.googleSignInSkipped
                ? 'new.firebase.create.beforeContinue.step1'
                : 'new.firebase.create.beforeContinue.step1.noAuth';
              const lastReadyKey = lastResult.googleSignInSkipped
                ? 'new.firebase.create.beforeContinue.ready'
                : 'new.firebase.create.beforeContinue.ready.noAuth';
              showBeforeContinue(step1Key, authUrl);
              openUrl(authUrl);
              await askReady(lastReadyKey);
            }

            break;
          } else {
            // Loop will replace ps2 next iteration; close the failed step now
            // so the previous spinner doesn't stay in the running state visually.
            ps2.fail(tr('new.firebase.create.failed'));
          }
        }
        if (!lastResult.ok && !lastResult.billingFailed) {
          const isProjectQuota = /exceeded your allotted project quota|project quota/i.test(String(lastResult.error || ''));
          const msg = isProjectQuota ? tr('new.firebase.create.projectQuotaExceeded') : `${tr('new.firebase.create.failed')}: ${lastResult.error}`;
          ui.log.error(msg);
          if (lastResult.projectId) {
            core.firebaseProjectId = lastResult.projectId;
            ui.log.message(tr('new.firebase.create.usingProjectId', { id: lastResult.projectId }));
          } else {
            core.firebaseProjectId = await ui.text({
              message: tr('new.firebase.q.projectId'),
              placeholder: tr('new.firebase.q.projectId.hint'),
              validate: (v) => (v && v.trim() ? undefined : tr('new.firebase.q.projectId.required')),
              onCancel: cancel,
            });
          }
        }
      }
    }
  }

  // ── Firebase: create from scratch for Supabase/API (push notifications only) ─
  if ((backend === 'supabase' || backend === 'api') && firebaseSetupMode === 'create') {
    const gcloudCheck = await checkGcloudAuth();
    if (!gcloudCheck.ok) {
      ui.log.error(tr('new.firebase.create.gcloudRequired'));
      if (gcloudCheck.missing === 'gcloud') {
        const instructions = getGcloudInstallInstructions();
        const noteLines = [tr('new.firebase.create.installTitle')];
        if (instructions.install) noteLines.push(`${tr('new.firebase.create.installCommand')}:\n  ${kleur.cyan(instructions.install)}`);
        if (instructions.hint) noteLines.push(instructions.hint);
        noteLines.push(`${tr('new.firebase.create.installAfter')}:\n  ${kleur.cyan(instructions.after)}`);
        noteLines.push(`${tr('new.firebase.create.installUrl')}: ${instructions.url}`);
        ui.note(noteLines.join('\n\n'));
      } else {
        ui.note(tr('new.firebase.create.authCommand'));
      }
      ui.log.warn(tr('new.firebase.create.fallbackHint'));
      core.firebaseProjectId = await ui.text({
        message: tr('new.firebase.q.projectId'),
        placeholder: tr('new.firebase.q.projectId.hint') + ' (FCM + Remote Config)',
        validate: (v) => (v && v.trim() ? undefined : tr('new.firebase.q.projectId.required')),
        onCancel: cancel,
      });
    } else {
      const selectedOrgId = await promptOrganizationIfNeeded(tr, cancel);
      const selectedBillingId = await promptBillingAccountIfNeeded(tr, cancel);
      ui.log.info(tr('new.internet.warning'));
      const ps3 = ui.makeTimedStepper();
      ps3.next(tr('new.firebase.create.creatingPush'));
      const setupResult = await setupFromScratch(core.appName.trim(), core.bundleId.trim(), {
        includeWeb: true,
        region: firebaseRegion,
        tr,
        billingAccountId: selectedBillingId || undefined,
        organizationId: selectedOrgId || undefined,
        onProgress: (key) => {
          if (key === 'wait-propagate') {
            ps3.next(tr('new.firebase.create.waitPropagate'));
          } else if (key === 'firestore') {
            ps3.next(stepProgress('firestore', language));
          } else if (key === 'storage') {
            ps3.next(stepProgress('storage', language));
          }
        },
      });
      if (setupResult.ok) {
        ps3.succeed(tr('new.firebase.create.successPush'));
        core.firebaseProjectId = setupResult.projectId;
        ui.log.info(`Project ID: ${core.firebaseProjectId}`);
        printCreateFromScratchStatus(setupResult, tr);
      } else {
        ps3.fail(tr('new.firebase.create.failed'));
        ui.log.error(`${tr('new.firebase.create.failed')}: ${setupResult.error}`);
        if (setupResult.projectId) {
          core.firebaseProjectId = setupResult.projectId;
          ui.log.message(tr('new.firebase.create.usingProjectId', { id: setupResult.projectId }));
        } else {
          core.firebaseProjectId = await ui.text({
            message: tr('new.firebase.q.projectId'),
            placeholder: tr('new.firebase.q.projectId.hint') + ' (FCM + Remote Config)',
            validate: (v) => (v && v.trim() ? undefined : tr('new.firebase.q.projectId.required')),
            onCancel: cancel,
          });
        }
      }
    }
  }

  // ── Backend-specific questions ───────────────────────────────────────────
  let supabaseCreate = false;
  let supabaseDbPassword = null;
  let supabaseCreateResult = null;
  /** When using existing project: { projectRef, supabaseUrl, supabaseAnonKey, dbPassword } */
  let supabaseExistingResult = null;

  if (backend === 'supabase') {
    supabaseCreate = await ui.select({
      message: tr('new.supabase.q.create'),
      initialValue: true,
      options: [
        { value: true,  label: tr('new.supabase.q.create.create') },
        { value: false, label: tr('new.supabase.q.create.existing') },
      ],
      onCancel: cancel,
    });

    const showLoginRequired = () => {
      ui.log.warn(`${tr('new.supabase.loginRequired')}\n${kleur.cyan(tr('new.supabase.loginCommand'))}`);
    };

    if (supabaseCreate) {
      const loginCheck = await checkLoggedIn();
      if (!loginCheck.ok) showLoginRequired();
      const orgsResult = await getOrgsList();
      if (!orgsResult.ok || !orgsResult.orgs?.length) {
        ui.log.error(tr('new.supabase.orgsRequired'));
        supabaseCreateResult = { ok: false, error: tr('new.supabase.orgsRequired') };
      } else {
        let orgId = orgsResult.orgs[0].id;
        if (orgsResult.orgs.length > 1) {
          orgId = await ui.select({
            message: tr('new.supabase.q.orgSelect'),
            initialValue: orgsResult.orgs[0].id,
            options: orgsResult.orgs.map((o) => ({ value: o.id, label: o.name })),
            onCancel: cancel,
          });
        }
        let supabaseRegion = DEFAULT_SUPABASE_REGION;
        if (!isQuick) {
          const region = await ui.select({
            message: tr('new.supabase.q.region'),
            initialValue: 'sa-east-1',
            options: [
              { value: 'sa-east-1', label: tr('new.supabase.q.region.brazil') },
              { value: 'us-east-1', label: tr('new.supabase.q.region.us') },
              { value: 'eu-west-1', label: tr('new.supabase.q.region.europe') },
            ],
            onCancel: cancel,
          });
          supabaseRegion = region || DEFAULT_SUPABASE_REGION;
        }
        const dbPassword = await ui.password({
          message: tr('new.supabase.q.dbPassword'),
          validate: (v) => (v && v.length >= 6 ? undefined : tr('new.supabase.q.dbPassword.required')),
          onCancel: cancel,
        });
        supabaseDbPassword = dbPassword;
        ui.log.info(tr('new.internet.warning'));
        const createSpinner = ui.timedSpinner();
        createSpinner.start(tr('new.supabase.creating'));
        supabaseCreateResult = await createProjectAndGetKeys(
          core.appName.trim().replace(/\s+/g, '-').toLowerCase(),
          supabaseDbPassword,
          supabaseRegion,
          orgId
        );
        createSpinner.stop(tr('new.supabase.creating'));
      }
      if (supabaseCreateResult.ok) {
        core.supabaseUrl = supabaseCreateResult.supabaseUrl;
        core.supabaseAnonKey = supabaseCreateResult.supabaseAnonKey;
        ui.log.success(tr('new.supabase.created'));
      } else {
        ui.log.error(`${tr('new.supabase.createFailed')}: ${supabaseCreateResult.error}`);
        ui.log.message(tr('new.supabase.loginHint'));
        Object.assign(core, await promptSupabaseManual(tr, cancel));
        supabaseCreate = false;
      }
    } else {
      // Usar projeto Supabase existente: org → projeto → keys → senha (mesmo fluxo de setup, sem criar)
      const loginCheck = await checkLoggedIn();
      if (!loginCheck.ok) {
        showLoginRequired();
        Object.assign(core, await promptSupabaseManual(tr, cancel));
      } else {
        const orgsResult = await getOrgsList();
        if (!orgsResult.ok || !orgsResult.orgs?.length) {
          ui.log.error(tr('new.supabase.orgsRequired'));
          Object.assign(core, await promptSupabaseManual(tr, cancel));
        } else {
          let orgId = orgsResult.orgs[0].id;
          if (orgsResult.orgs.length > 1) {
            orgId = await ui.select({
              message: tr('new.supabase.q.useExisting.orgSelect'),
              initialValue: orgsResult.orgs[0].id,
              options: orgsResult.orgs.map((o) => ({ value: o.id, label: o.name })),
              onCancel: cancel,
            });
          }
          const projectsResult = await getProjectsByOrg(orgId);
          if (!projectsResult.ok || !projectsResult.projects?.length) {
            ui.log.warn(tr('new.supabase.projectsRequired'));
            Object.assign(core, await promptSupabaseManual(tr, cancel));
          } else {
            const selectedProjectRef = await ui.select({
              message: tr('new.supabase.q.useExisting.projectSelect'),
              initialValue: projectsResult.projects[0].id,
              options: projectsResult.projects.map((p) => ({ value: p.id, label: `${p.name} (${p.id})` })),
              onCancel: cancel,
            });
            const keysResult = await getProjectKeys(selectedProjectRef);
            if (!keysResult.ok) {
              ui.log.error(keysResult.error);
              Object.assign(core, await promptSupabaseManual(tr, cancel));
            } else {
              core.supabaseUrl = keysResult.supabaseUrl;
              core.supabaseAnonKey = keysResult.supabaseAnonKey;
              const dbPassword = await ui.password({
                message: tr('new.supabase.q.dbPassword.existing'),
                validate: (v) => (v && v.length >= 6 ? undefined : tr('new.supabase.q.dbPassword.required')),
                onCancel: cancel,
              });
              supabaseExistingResult = {
                ok: true,
                projectRef: selectedProjectRef,
                supabaseUrl: keysResult.supabaseUrl,
                supabaseAnonKey: keysResult.supabaseAnonKey,
                dbPassword,
              };
              ui.log.success(tr('new.supabase.existingLinked'));
            }
          }
        }
      }
    }
  }

  // ── Supabase: Google Sign-In credentials resolved automatically after build ─
  // (No prompts — web/iOS client IDs come from google-services.json /
  //  GoogleService-Info.plist; secret fetched from Identity Toolkit API)
  let googleWebClientId = '';
  let googleClientSecret = '';
  let googleIosClientId = '';

  if (backend === 'api') {
    core.apiBaseUrl = await ui.text({
      message: tr('new.api.q.baseUrl'),
      placeholder: tr('new.api.q.baseUrl.hint'),
      initialValue: 'https://api.example.com',
      onCancel: cancel,
    });
  }

  // ── Firebase existing project: enable APIs + create Firestore/Storage ───
  if (backend === 'firebase' && firebaseSetupMode === 'existing' && core.firebaseProjectId) {
    const ps4 = ui.makeTimedStepper();
    ps4.next(stepProgress('enable-apis', language));
    const existingSetup = await setupExistingProject(core.firebaseProjectId, {
      onProgress: (key, data) => {
        if (key === 'enable-apis') {
          ps4.next(stepProgress('enable-apis', language));
        } else if (key === 'enable-apis-warn') {
          ps4.warn(`${tr('new.firebase.create.failed')}: APIs`);
          ui.log.warn(`${tr('new.firebase.existing.apisFailed')} ${(data?.error || '').slice(0, 80)}`);
        } else if (key === 'firestore') {
          ps4.next(stepProgress('firestore', language));
        } else if (key === 'storage') {
          ps4.next(stepProgress('storage', language));
        } else if (key === 'auth-providers-warn') {
          ps4.stop();
          ui.log.warn(`${tr('new.firebase.interactive.authWarn')}\n${kleur.cyan(data?.url || '')}`);
        } else if (key === 'auth-google-warn') {
          ps4.stop();
          ui.log.warn(`${tr('new.firebase.existing.googleSignInManual')}\n${kleur.cyan(data?.url || '')}`);
        }
      },
    });
    if (existingSetup.firestoreCreated) {
      ps4.succeed(stepLabel('firestore', language));
    } else if (existingSetup.firestoreError && !existingSetup.firestoreError.includes('ALREADY_EXISTS')) {
      ps4.fail(`Firestore: ${(existingSetup.firestoreError || '').slice(0, 80)}`);
      ui.log.message(kleur.cyan(existingSetup.firestoreUrl));
    } else {
      ps4.stop();
    }
    if (existingSetup.storageCreated) {
      ui.log.success(stepLabel('storage', language));
    } else if (existingSetup.storageError && !existingSetup.storageError.includes('ALREADY_EXISTS')) {
      ui.log.warn(`Storage: ${(existingSetup.storageError || '').slice(0, 80)}\n${kleur.cyan(existingSetup.storageUrl)}`);
    }
  }

  // ── Optional modules ────────────────────────────────────────────────────
  // --with flag pre-selects modules and bypasses preset selection entirely.
  const preselectedModules = withModules
    ? String(withModules).split(',').map((m) => m.trim().toLowerCase()).filter(Boolean)
    : [];

  let modules = [];

  if (preselectedModules.length > 0) {
    // --with flag was passed: use those modules directly, skip preset prompt.
    modules = preselectedModules;
  } else if (isQuick) {
    // Quick mode: show preset picker (no multiselect).
    const preset = await ui.select({
      message: tr('new.q.preset'),
      initialValue: 'starter',
      options: [
        { value: 'starter', label: tr('new.q.preset.starter') },
        { value: 'saas',    label: tr('new.q.preset.saas') },
        { value: 'content', label: tr('new.q.preset.content') },
        { value: 'full',    label: tr('new.q.preset.full') },
        { value: 'none',    label: tr('new.q.preset.none') },
      ],
      onCancel: cancel,
    });
    modules = MODULE_PRESETS[preset] || [];
  } else {
    // Advanced mode: full multiselect — built from catalog, filtered by audience + backend.
    const visibleFeatures = getVisibleFeatures({ audience: KASY_AUDIENCE, backend });

    // web has an extra runtime constraint: firebase create-mode only
    const isWebExcluded = backend === 'firebase' && firebaseSetupMode === 'create';

    // Visual groups in display order (header key → feature ids in this group)
    const groups = [
      { header: null,                              ids: ['sentry', 'analytics', 'facebook'] },
      { header: 'new.modules.header.monetization', ids: ['revenuecat'] },
      { header: 'new.modules.header.features',     ids: ['onboarding', 'web', 'widget', 'llm_chat', 'local_notifications'] },
      { header: 'new.modules.header.feedback',     ids: ['feedback'] },
      { header: 'new.modules.header.ci',           ids: ['ci'] },
    ];

    // Clack multiselect does not support disabled separator rows, so we
    // prefix each option with its group name (e.g. "Monetização · RevenueCat").
    const moduleOptions = [];
    for (const group of groups) {
      const inGroup = visibleFeatures.filter((f) => {
        if (!group.ids.includes(f.id)) return false;
        if (f.id === 'web' && isWebExcluded) return false;
        return true;
      });
      if (inGroup.length === 0) continue;
      const groupLabel = group.header ? tr(group.header) : null;
      for (const f of inGroup) {
        const moduleLabel = tr(`new.firebase.module.${f.id}`) + (f.status === 'internal' ? ' [beta]' : '');
        const label = groupLabel
          ? `${kleur.dim(groupLabel + ' · ')}${moduleLabel}`
          : moduleLabel;
        moduleOptions.push({ value: f.id, label });
      }
    }

    const rawModules = await ui.multiselect({
      message: tr('new.firebase.q.modules'),
      options: moduleOptions,
      initialValues: [],
      required: false,
      onCancel: cancel,
    });
    modules = rawModules || [];
  }

  if (backend === 'firebase' && firebaseSetupMode === 'create' && firebaseIncludeWeb) {
    modules = [...new Set([...modules, 'web'])];
  }

  // ── Module-specific questions ───────────────────────────────────────────
  const moduleAnswers = {};

  if (modules.includes('revenuecat')) {
    // Three keys, all optional, but we require at least one. The kasy run
    // command picks the right key based on the device:
    //   simulator/emulator → RC_TEST_KEY
    //   physical iOS       → RC_IOS_PROD_KEY (falls back to test if missing)
    //   physical Android   → RC_ANDROID_PROD_KEY (falls back to test if missing)
    moduleAnswers.rcTestKey = ((await ui.text({
      message: tr('new.firebase.q.revenuecat.test'),
      validate: (v) => {
        const s = (v || '').trim();
        if (!s) return undefined;
        return /^test_/.test(s) ? undefined : tr('new.firebase.q.revenuecat.test.invalid');
      },
      onCancel: cancel,
    })) || '').trim();
    moduleAnswers.rcIosProdKey = ((await ui.text({
      message: tr('new.firebase.q.revenuecat.iosProd'),
      validate: (v) => {
        const s = (v || '').trim();
        if (!s) return undefined;
        return /^appl_/.test(s) ? undefined : tr('new.firebase.q.revenuecat.iosProd.invalid');
      },
      onCancel: cancel,
    })) || '').trim();
    moduleAnswers.rcAndroidProdKey = ((await ui.text({
      message: tr('new.firebase.q.revenuecat.androidProd'),
      validate: (v) => {
        const s = (v || '').trim();
        if (!s) return undefined;
        return /^goog_/.test(s) ? undefined : tr('new.firebase.q.revenuecat.androidProd.invalid');
      },
      onCancel: cancel,
    })) || '').trim();
    if (!moduleAnswers.rcTestKey && !moduleAnswers.rcIosProdKey && !moduleAnswers.rcAndroidProdKey) {
      // Non-blocking: user can fill .env later. Just warn so they're not surprised.
      ui.log.warn(tr('new.firebase.q.revenuecat.atLeastOne'));
    }
    moduleAnswers.defaultPaywall = await ui.select({
      message: tr('new.firebase.q.paywall'),
      initialValue: 'basic',
      options: [
        { value: 'basic',      label: 'Basic (list of plans)' },
        { value: 'withSwitch', label: 'With trial switch' },
        { value: 'basicRow',   label: 'Row + comparison table' },
        { value: 'minimal',    label: 'Minimal (benefits + CTA)' },
      ],
      onCancel: cancel,
    });
  }

  // RC web key — only in advanced mode (optional credential, can configure later).
  if (!isQuick && modules.includes('revenuecat') && modules.includes('web')) {
    const rcWebKey = await ui.text({
      message: tr('new.firebase.q.revenuecat.webKey'),
      validate: (v) => {
        if (!v || !v.trim()) return undefined; // optional — blank is fine
        return /^rcb_/.test(v.trim()) ? undefined : tr('new.firebase.q.revenuecat.webKey.invalid');
      },
      onCancel: cancel,
    });
    moduleAnswers.revenuecatWeb = true;
    moduleAnswers.rcWebKey = (rcWebKey || '').trim();
  } else if (modules.includes('revenuecat') && modules.includes('web')) {
    // Quick mode: mark web billing as included but key will be configured later.
    moduleAnswers.revenuecatWeb = true;
    moduleAnswers.rcWebKey = '';
  }

  // Sentry DSN — already optional (blank = configure later). Skip in quick mode.
  if (!isQuick && modules.includes('sentry')) {
    moduleAnswers.sentryDsn = await ui.text({
      message: tr('new.firebase.q.sentry.dsn'),
      validate: (v) => {
        if (!v || !v.trim()) return undefined; // optional — blank is fine
        return /^https?:\/\/[^@\s]+@[^/\s]+\/\d+$/.test(v.trim())
          ? undefined
          : tr('new.firebase.q.sentry.dsn.invalid');
      },
      onCancel: cancel,
    });
  }

  // Mixpanel token — already optional. Skip in quick mode.
  if (!isQuick && modules.includes('analytics')) {
    moduleAnswers.mixpanelToken = await ui.text({
      message: tr('new.firebase.q.mixpanel.token'),
      onCancel: cancel,
    });
  }

  // LLM Chat credentials — skip in quick mode, all fields optional.
  if (!isQuick && modules.includes('llm_chat')) {
    const configureLlmNow = await ui.confirm({
      message: tr('new.q.llm_chat.configureNow'),
      initialValue: true,
      onCancel: cancel,
    });

    if (configureLlmNow) {
      moduleAnswers.llmProvider = await ui.select({
        message: tr('add.prompt.llmProvider'),
        initialValue: 'openai',
        options: [
          { value: 'openai', label: 'OpenAI (gpt-4o-mini)' },
          { value: 'gemini', label: 'Google Gemini (gemini-1.5-flash)' },
        ],
        onCancel: cancel,
      });
      moduleAnswers.llmSystemPrompt = await ui.text({
        message: tr('add.prompt.llmSystemPrompt'),
        onCancel: cancel,
      });
      moduleAnswers.llmApiKey = await ui.password({
        message: tr('add.prompt.llmApiKey'),
        onCancel: cancel,
      });
    } else {
      moduleAnswers.llmConfigureLater = true;
    }
  } else if (isQuick && modules.includes('llm_chat')) {
    moduleAnswers.llmConfigureLater = true;
  }

  // Facebook — required credentials, always ask.
  if (modules.includes('facebook')) {
    moduleAnswers.fbAppId = await ui.text({
      message: tr('new.firebase.q.facebook.appId'),
      validate: (v) => {
        if (!v || !v.trim()) return tr('new.firebase.q.facebook.appId.required');
        return /^\d+$/.test(v.trim()) ? undefined : tr('new.firebase.q.facebook.appId.invalid');
      },
      onCancel: cancel,
    });
    moduleAnswers.fbToken = await ui.password({
      message: tr('new.firebase.q.facebook.token'),
      validate: (v) => (v && v.trim() ? undefined : tr('new.firebase.q.facebook.token.required')),
      onCancel: cancel,
    });
  }

  // Server secrets (webhook, Meta Ads) — skip in quick mode, configure later via `kasy deploy`.
  if (!isQuick && modules.includes('revenuecat') && (backend === 'supabase' || backend === 'firebase')) {
    const configureSecretsNow = await ui.confirm({
      message: tr('new.firebase.q.secrets.configureNow'),
      initialValue: true,
      onCancel: cancel,
    });

    if (configureSecretsNow) {
      moduleAnswers.rcWebhookKey = await ui.text({
        message: tr('new.firebase.q.revenuecat.webhookKey'),
        initialValue: generateWebhookKey(),
        placeholder: tr('new.firebase.q.revenuecat.webhookKey.hint'),
        onCancel: cancel,
      });
      moduleAnswers.metaAccessToken = await ui.password({
        message: tr('new.firebase.q.revenuecat.metaToken'),
        onCancel: cancel,
      });
      moduleAnswers.metaDatasetId = await ui.text({
        message: tr('new.firebase.q.revenuecat.metaDataset'),
        validate: (v) => {
          if (!v || !v.trim()) return undefined; // optional — blank is fine
          return /^\d+$/.test(v.trim()) ? undefined : tr('new.firebase.q.revenuecat.metaDataset.invalid');
        },
        onCancel: cancel,
      });
    } else {
      moduleAnswers.secretsConfigureLater = true;
    }
  } else if (isQuick && modules.includes('revenuecat')) {
    // Quick mode: always defer secrets to `kasy deploy`.
    moduleAnswers.secretsConfigureLater = true;
  }

  // ── Deploy is now a separate command (`kasy deploy`) ─────────────
  // Deploy is never run during `new` — the project is generated and ready,
  // and the user deploys when they choose with `kasy deploy`.
  const deploy = false;

  // ── Summary ─────────────────────────────────────────────────────────────
  const answers = {
    appName: core.appName.trim(),
    bundleId: core.bundleId.trim(),
    firebaseProjectId: core.firebaseProjectId.trim(),
    modules: modules || [],
    backend,
    includeWeb: backend === 'firebase' ? firebaseIncludeWeb : true,
    supabaseUrl: core.supabaseUrl?.trim(),
    supabaseAnonKey: core.supabaseAnonKey?.trim(),
    apiBaseUrl: core.apiBaseUrl?.trim(),
  };

  printSummary(tr, answers);

  if (!yes) {
    const proceed = await ui.confirm({
      message: tr('new.firebase.confirm.proceed'),
      initialValue: true,
      onCancel: cancel,
    });
    if (!proceed) {
      ui.cancel(tr('prompt.cancelled'));
      process.exit(0);
    }
  }

  // ── Generate ────────────────────────────────────────────────────────────
  // Stepper shows each step closing as ✦ and the next starting as ⠙ — so the
  // user gets explicit "X done → Y starting" feedback instead of a single
  // spinner with a mutating message.
  const stepper = ui.makeTimedStepper();
  // First step started here so even silent prep work shows progress.
  stepper.next(stepProgress('project-setup', language));

  const onProgress = (key) => {
    // Each onProgress closes previous step with ✦ and starts the new one.
    stepper.next(stepProgress(key, language));
  };

  let result;
  try {
    if (backend === 'supabase') {
      result = await generateSupabaseProject(targetDir, {
        appName: answers.appName,
        bundleId: answers.bundleId,
        firebaseProjectId: answers.firebaseProjectId,
        supabaseUrl: answers.supabaseUrl,
        supabaseAnonKey: answers.supabaseAnonKey,
        modules: answers.modules,
        moduleAnswers,
        language,
        onProgress,
      });
    } else if (backend === 'api') {
      result = await generateApiProject(targetDir, {
        appName: answers.appName,
        bundleId: answers.bundleId,
        firebaseProjectId: answers.firebaseProjectId,
        apiBaseUrl: answers.apiBaseUrl,
        modules: answers.modules,
        moduleAnswers,
        language,
        onProgress,
      });
    } else {
      result = await generateFirebaseProject(targetDir, {
        appName: answers.appName,
        bundleId: answers.bundleId,
        firebaseProjectId: answers.firebaseProjectId,
        deploy,
        modules: answers.modules,
        moduleAnswers,
        includeWeb: answers.includeWeb !== false,
        functionsRegion: firebaseRegion,
        language,
        onProgress,
      });
    }
    // Close the last in-flight step. We don't need a label — its own message
    // is already shown next to the ✦ by Clack.
    stepper.succeed();
  } catch (err) {
    stepper.fail(err.message);
    throw err;
  }

  // Save deploy metadata for `kasy deploy` auto-detect
  if (backend === 'firebase') {
    try {
      const kasyDir = path.join(targetDir, '.kasy');
      await fs.ensureDir(kasyDir);
      await fs.writeJson(path.join(kasyDir, 'config.json'), { functionsRegion: firebaseRegion }, { spaces: 2 });
    } catch (_) {}

    // Write functions/.env with LLM non-secret vars (provider + system prompt)
    if (modules.includes('llm_chat') && !moduleAnswers.llmConfigureLater) {
      try {
        const envLines = [];
        if (moduleAnswers.llmProvider) envLines.push(`LLM_PROVIDER=${moduleAnswers.llmProvider}`);
        if (moduleAnswers.llmSystemPrompt?.trim()) envLines.push(`LLM_SYSTEM_PROMPT=${moduleAnswers.llmSystemPrompt.trim()}`);
        if (envLines.length > 0) {
          await fs.ensureDir(path.join(targetDir, 'functions'));
          await fs.appendFile(path.join(targetDir, 'functions', '.env'), envLines.join('\n') + '\n', 'utf8');
        }
      } catch (_) {}
    }
  }

  // ── Results ─────────────────────────────────────────────────────────────
  // Stepper already announced each successful step. Print only failures and
  // skipped steps here, so the user notices what didn't run without seeing
  // every success twice.
  result.steps
    .filter((s) => s.skipped || !s.ok)
    .forEach((s) => printStepResult(s, language));

  // ── Supabase: link + db push + functions deploy (when project was created or existing selected) ─
  const supabaseSetupPayload = (supabaseCreate && supabaseCreateResult?.ok)
    ? { projectRef: supabaseCreateResult.projectRef, dbPassword: supabaseDbPassword }
    : (supabaseExistingResult?.ok ? { projectRef: supabaseExistingResult.projectRef, dbPassword: supabaseExistingResult.dbPassword } : null);

  if (backend === 'supabase') {
    // ── Auto-resolve Google credentials from flutterfire output files ────────
    const flutterfireOk = result.steps.find((s) => s.name === 'flutterfire')?.ok;
    if (flutterfireOk) {
      const fileCreds = await readSupabaseGoogleCredentials(targetDir);
      googleWebClientId = fileCreds.webClientId;
      googleIosClientId = fileCreds.iosClientId;

      // Enable Google Sign-In in Firebase Auth so Identity Toolkit stores the client secret.
      // This is the same step that setupFromScratch runs for Firebase backend.
      // For Supabase, Firebase Auth is not used for auth, but enabling Google here is the
      // only way to make the client secret available via the Identity Toolkit API.
      // Non-fatal: silently ignored if it fails (e.g. API not yet propagated).
      if (answers.firebaseProjectId) {
        await enableAuthProviders(answers.firebaseProjectId);
      }

      // Try to get the Client Secret from Identity Toolkit API (requires gcloud auth)
      if (answers.firebaseProjectId && googleWebClientId) {
        googleClientSecret = await getGoogleClientSecretViaGcloud(answers.firebaseProjectId);
      }

      // Always write google_auth_options.dart with whatever credentials we have
      if (googleWebClientId) {
        const gaResult = await writeSupabaseGoogleAuthOptions(targetDir, googleWebClientId, googleIosClientId);
        printStepResult({ name: 'google-auth-options', ok: gaResult.ok, detail: gaResult.error }, language);
      }
    }

    if (supabaseSetupPayload) {
      // ── FCM Service Account key (best effort via gcloud — Firebase uses ADC, Supabase needs JSON) ──
      let fcmServiceAccountJson = null;
      if (answers.firebaseProjectId) {
        const fcmSpinner = ui.timedSpinner();
        fcmSpinner.start(tr('new.fcm.generating'));
        const fcmResult = await createFcmServiceAccountKey(answers.firebaseProjectId);
        fcmSpinner.stop(tr('new.fcm.generating'));
        if (fcmResult.ok) {
          fcmServiceAccountJson = fcmResult.json;
          printStepResult({ name: 'fcm-key', ok: true }, language);
        } else {
          printStepResult({ name: 'fcm-key', ok: false, detail: 'configure FIREBASE_SERVICE_ACCOUNT_JSON manualmente' }, language);
        }
      }

      const setupSpinner = ui.spinner();
      setupSpinner.start(tr('new.supabase.setup'));
      try {
        const secrets = {
          firebaseProjectId: answers.firebaseProjectId,
          ...(fcmServiceAccountJson ? { firebaseServiceAccountJson: fcmServiceAccountJson } : {}),
          ...(answers.modules?.includes('revenuecat') ? {
            rcWebhookKey: moduleAnswers.rcWebhookKey,
            metaAccessToken: moduleAnswers.metaAccessToken,
            metaDatasetId: moduleAnswers.metaDatasetId,
          } : {}),
          ...(answers.modules?.includes('llm_chat') && !moduleAnswers.llmConfigureLater ? {
            llmApiKey: moduleAnswers.llmApiKey,
            llmProvider: moduleAnswers.llmProvider,
            llmSystemPrompt: moduleAnswers.llmSystemPrompt,
          } : {}),
        };
        const google = googleWebClientId && googleClientSecret
          ? { webClientId: googleWebClientId, clientSecret: googleClientSecret }
          : {};
        const setupSteps = await setupLinkedProject(
          targetDir,
          supabaseSetupPayload.projectRef,
          supabaseSetupPayload.dbPassword,
          secrets,
          google
        );
        setupSpinner.stop(tr('new.supabase.setup'));
        setupSteps.forEach((s) => printStepResult(s, language));
      } catch (err) {
        setupSpinner.error(err.message);
        ui.log.warn(tr('new.supabase.setupManual'));
      }
    }
  }

  // ── API: FCM Service Account key — save to .kasy/ for server configuration ──
  if (backend === 'api' && answers.firebaseProjectId) {
    const fcmSpinner = ui.timedSpinner();
    fcmSpinner.start(tr('new.fcm.generating'));
    const fcmResult = await createFcmServiceAccountKey(answers.firebaseProjectId);
    fcmSpinner.stop(tr('new.fcm.generating'));
    if (fcmResult.ok) {
      try {
        const kasyDir = path.join(targetDir, '.kasy');
        await fs.ensureDir(kasyDir);
        await fs.writeFile(path.join(kasyDir, 'fcm-service-account.json'), fcmResult.json, 'utf8');
        // Ensure this credential file is not committed to git
        const gitignorePath = path.join(targetDir, '.gitignore');
        const gitignoreEntry = '.kasy/fcm-service-account.json';
        if (await fs.pathExists(gitignorePath)) {
          const existing = await fs.readFile(gitignorePath, 'utf8');
          if (!existing.includes(gitignoreEntry)) {
            await fs.appendFile(gitignorePath, `\n# FCM service account key (credentials — do not commit)\n${gitignoreEntry}\n`, 'utf8');
          }
        }
        printStepResult({ name: 'fcm-key-saved', ok: true }, language);
        ui.log.message(tr('new.fcm.serverConfig'));
      } catch (_) {
        printStepResult({ name: 'fcm-key-saved', ok: false, detail: 'salvar arquivo de chave falhou' }, language);
      }
    } else {
      printStepResult({ name: 'fcm-key', ok: false, detail: 'configure FIREBASE_SERVICE_ACCOUNT_JSON manualmente' }, language);
    }
  }

  // ── SHA-1: register debug keystore for existing Firebase projects (all backends) ──
  // setupFromScratch already handles SHA-1 for new-project flow.
  // For existing projects, flutterfire configure registers the Android app but does NOT add SHA-1.
  // Without SHA-1, google_sign_in v7+ Credential Manager fails on Android with:
  // "CredManProvService: GetCredentialResponse error returned from framework"
  if (answers.firebaseProjectId && firebaseSetupMode === 'existing') {
    const flutterfireOkForSha1 = result.steps.find((s) => s.name === 'flutterfire')?.ok;
    if (flutterfireOkForSha1) {
      const sha1Spinner = ui.spinner();
      sha1Spinner.start(tr('new.sha1.registering'));
      const sha1Result = await registerDebugSha1(answers.firebaseProjectId, answers.bundleId);
      sha1Spinner.stop(tr('new.sha1.registering'));
      if (sha1Result.ok) {
        if (!sha1Result.existed) {
          printStepResult({ name: 'sha1', ok: true }, language);
        }
        // existed === true → already registered, silent success
      } else {
        const sha1ManualUrl = `https://console.firebase.google.com/project/${answers.firebaseProjectId}/settings/general/android:${answers.bundleId}`;
        ui.log.warn(
          `${tr('new.sha1.failed', { error: (sha1Result.sha1Error || '').slice(0, 120) })}\n${tr('new.sha1.manual')}\n${kleur.cyan(sha1ManualUrl)}`
        );
        openUrl(sha1ManualUrl);
      }
    }
  }

  const flutterfireStep = result.steps.find((s) => s.name === 'flutterfire');
  if (flutterfireStep && !flutterfireStep.ok) {
    const platforms = answers.includeWeb !== false ? 'android,ios,web' : 'android,ios';
    const cmd = `dart pub global run flutterfire_cli:flutterfire configure --project=${answers.firebaseProjectId} --out lib/firebase_options_dev.dart --platforms=${platforms} --yes`;
    ui.log.warn(
      `${tr('new.firebase.warn.flutterfire')}\n${tr('new.firebase.warn.flutterfire.manual')}\n${kleur.cyan(cmd)}`
    );
  }

  // ── APNs reminder (all backends — required for iOS push notifications) ───────
  // Cannot be automated: the .p8 key only exists in Apple Developer Portal.
  if (answers.firebaseProjectId) {
    const apnsUrl = `https://console.firebase.google.com/project/${answers.firebaseProjectId}/settings/cloudmessaging`;
    ui.log.warn(
      `${tr('new.apns.warning')}\n${tr('new.apns.step1')}\n${tr('new.apns.step2')}\n${kleur.cyan(apnsUrl)}`
    );
    openUrl(apnsUrl);
  }

  printSuccessCard(tr, answers, targetDir);

  ui.outro(kleur.bold(tr('new.success.title')));
}

module.exports = { runNew };
