const { exec } = require('node:child_process');
const { promisify } = require('node:util');
const kleur = require('kleur');
const ui = require('./ui');
const { createTranslator, detectDefaultLanguage } = require('./i18n');

const execAsync = promisify(exec);

// Timeout para verificar se uma ferramenta está instalada (15 s é mais que suficiente)
const TOOL_CHECK_TIMEOUT = 15_000;
// Timeout para instalação automática de ferramentas (5 min)
const INSTALL_TIMEOUT = 300_000;

// Minimum supported versions for Kasy projects
const MIN_NODE_VERSION = '18.0.0';
const MIN_FLUTTER_VERSION = '3.24.0';
const MIN_DART_VERSION = '3.5.0';

const BASE_CHECKS = [
  {
    name: 'Node.js',
    command: 'node --version',
    required: true,
    minVersion: MIN_NODE_VERSION,
  },
  {
    name: 'Flutter SDK',
    command: 'flutter --version',
    required: false,
    warnMessageKey: 'checks.flutter.warn',
    minVersion: MIN_FLUTTER_VERSION,
  },
  {
    name: 'Dart SDK',
    command: 'dart --version',
    required: false,
    minVersion: MIN_DART_VERSION,
  }
];

const PLATFORM_CHECKS = {
  darwin: [
    {
      name: 'Ruby',
      command: 'ruby -v',
      required: false
    },
    {
      name: 'CocoaPods',
      command: 'pod --version',
      required: false
    }
  ],
  win32: [],
  linux: []
};

function getGcloudInstallHint() {
  switch (process.platform) {
    case 'darwin':
      return 'brew install --cask google-cloud-sdk';
    case 'linux':
      return 'curl https://sdk.cloud.google.com | bash && source ~/.bashrc';
    case 'win32':
      return 'winget install Google.CloudSDK\n    ou baixe o instalador: https://cloud.google.com/sdk/docs/install-sdk#windows';
    default:
      return 'https://cloud.google.com/sdk/docs/install';
  }
}

/** Firebase CLI + FlutterFire — required for push notifications (FCM) on all backends */
const FIREBASE_CHECKS = [
  {
    name: 'Firebase CLI',
    command: 'firebase --version',
    required: true,
    tryInstall: 'npm install -g firebase-tools',
    tryInstallMessageKey: 'setup.firebase.installing',
  },
  {
    name: 'FlutterFire CLI',
    command: 'flutterfire --version',
    required: true,
    tryInstall: 'dart pub global activate flutterfire_cli',
    retryCommand: `${process.env.HOME}/.pub-cache/bin/flutterfire --version`,
    tryInstallMessageKey: 'setup.flutterfire.installing',
  },
  {
    name: 'gcloud CLI (create-from-scratch)',
    command: 'gcloud --version',
    required: false,
    failHint: getGcloudInstallHint(),
    waitPromptKey: 'checks.waitPrompt.gcloud.install',
  },
  {
    name: 'gcloud auth (create-from-scratch)',
    command: 'gcloud auth print-access-token',
    required: false,
    showVersion: false,
    failHint: 'gcloud auth login',
    waitPromptKey: 'checks.waitPrompt.gcloud.auth',
  },
];

const BACKEND_CHECKS = {
  firebase: FIREBASE_CHECKS,
  supabase: [
    ...FIREBASE_CHECKS,
    {
      name: 'Supabase CLI',
      command: 'supabase --version',
      required: true,
      tryInstall: 'npm install -g supabase',
      tryInstallMessageKey: 'setup.supabase.installing',
    },
  ],
  restApi: FIREBASE_CHECKS
};

function getBaseChecks() {
  return [...BASE_CHECKS];
}

function getPlatformChecks() {
  return [...(PLATFORM_CHECKS[process.platform] || [])];
}

function getBackendChecks(backend) {
  return [...(BACKEND_CHECKS[backend] || [])];
}

function diagnoseFailure(err) {
  const text = `${err?.stderr || ''}\n${err?.stdout || ''}\n${err?.message || ''}`.toLowerCase();
  if (text.includes('xcode') && text.includes('license')) {
    return 'xcodeLicense';
  }
  if (text.includes('xcode-select') || text.includes('command line tools') || text.includes('no developer tools')) {
    return 'xcodeCli';
  }
  return null;
}

function extractVersion(stdout, checkName) {
  const raw = (stdout || '').trim();
  if (!raw) return null;
  if (checkName === 'Node.js') {
    const m = raw.match(/v?([\d.]+)/);
    return m ? `v${m[1]}` : raw.slice(0, 20);
  }
  if (checkName === 'Flutter SDK') {
    const m = raw.match(/Flutter\s+([\d.]+)/);
    return m ? m[1] : raw.split('\n')[0]?.slice(0, 30) || null;
  }
  if (checkName === 'Dart SDK') {
    const m = raw.match(/version:\s*([\d.]+)/);
    return m ? m[1] : raw.split('\n')[0]?.slice(0, 25) || null;
  }
  const m = raw.match(/[\d.]+/);
  return m ? m[0] : raw.slice(0, 20);
}

async function runSingleCheck(check, options = {}) {
  const showVersion = check.showVersion !== undefined ? check.showVersion : (options.showVersion !== undefined ? options.showVersion : true);
  let autoInstallFailed = false;

  try {
    const { stdout } = await execAsync(check.command, { encoding: 'utf8', timeout: TOOL_CHECK_TIMEOUT });
    const version = showVersion ? extractVersion(stdout, check.name) : null;
    return { ...check, ok: true, version: version || null };
  } catch (err) {
    const diagnosis = diagnoseFailure(err);
    if (check.tryInstall) {
      try {
        await execAsync(check.tryInstall, { encoding: 'utf8', timeout: INSTALL_TIMEOUT });
        const { stdout: retryOut } = await execAsync(check.retryCommand || check.command, { encoding: 'utf8', timeout: TOOL_CHECK_TIMEOUT });
        const version = showVersion ? extractVersion(retryOut, check.name) : null;
        return { ...check, ok: true, version: version || null };
      } catch {
        autoInstallFailed = true;
      }
    }
    return { ...check, ok: false, autoInstallFailed, diagnosis };
  }
}

/**
 * Retry a check after the user installs/auths a missing tool. Used when the
 * check definition has `waitPrompt` — we offer the user to run the install
 * step, then re-check on Enter. Returns true if the recheck succeeded.
 */
async function retryCheckInteractively(check, t) {
  ui.log.warn(`${check.name} ${t('checks.notFound.short') || 'not found'}`);
  if (check.failHint) {
    ui.log.message(`${t('checks.runHint') || 'Run'}: ${kleur.cyan(check.failHint)}`);
  }
  const proceed = await ui.confirm({
    message: check.waitPrompt,
    initialValue: true,
  });
  if (!proceed) return false;
  try {
    const { stdout } = await execAsync(check.command, { encoding: 'utf8', timeout: TOOL_CHECK_TIMEOUT });
    const version = extractVersion(stdout, check.name);
    ui.log.success(version
      ? `${check.name} — ${version}`
      : check.name);
    return true;
  } catch {
    ui.log.error(`${check.name} ${t('checks.stillMissing') || 'still missing'}`);
    return false;
  }
}

async function runChecks(checks, title, options = {}) {
  const t = options.t || createTranslator(options.language || detectDefaultLanguage());
  const { showVersion = true } = options;

  // Single spinner over all checks, show failures afterwards. The visual
  // sits inside the clack rail (│) opened by the caller's ui.intro().
  const { spinnerLabel = title, doneLabel = title } = options;
  const spinner = ui.spinner();
  spinner.start(spinnerLabel);

  const results = [];
  for (const check of checks) {
    results.push(await runSingleCheck({ ...check, t }, { showVersion }));
  }

  const failures = results.filter((r) => !r.ok);
  const requiredFailures = failures.filter((r) => r.required);

  if (failures.length === 0) {
    spinner.stop(doneLabel);
    return results;
  }

  // Close the spinner reflecting what actually happened: red ▲ if a required
  // check failed, default green ✦ if only optional checks failed (warnings).
  if (requiredFailures.length > 0) {
    spinner.error(doneLabel);
  } else {
    spinner.stop(doneLabel);
  }

  for (const result of failures) {
    // Interactive recovery for checks with a waitPrompt (e.g. gcloud install).
    // Lets the user fix the env without restarting the command.
    const waitPromptText = result.waitPromptKey ? t(result.waitPromptKey) : result.waitPrompt;
    if (waitPromptText) {
      const recovered = await retryCheckInteractively({ ...result, waitPrompt: waitPromptText }, t);
      if (recovered) {
        const idx = results.indexOf(result);
        if (idx >= 0) results[idx] = { ...result, ok: true };
      }
      continue;
    }
    const hint = result.failHint ? `\n${kleur.dim(`→ ${result.failHint}`)}` : '';
    if (result.required) {
      const detail = result.autoInstallFailed ? ` — ${t('checks.install.failed')}` : '';
      const diagSuffix = result.diagnosis ? `\n${kleur.dim(`→ ${t(`checks.diagnostic.${result.diagnosis}`, { name: result.name })}`)}` : '';
      ui.log.error(`${t('checks.missing', { name: result.name })}${detail}${diagSuffix}${hint}`);
    } else if (result.diagnosis) {
      ui.log.warn(t(`checks.diagnostic.${result.diagnosis}`, { name: result.name }));
    } else if (result.warnMessage) {
      ui.log.warn(`${result.warnMessage}${hint}`);
    } else if (result.warnMessageKey) {
      ui.log.warn(`${t(result.warnMessageKey)}${hint}`);
    } else {
      ui.log.warn(`${t('checks.notFound', { name: result.name })}${hint}`);
    }
  }

  return results;
}

function hasRequiredFailures(results) {
  return results.some((result) => result.required && !result.ok);
}

module.exports = {
  getBaseChecks,
  getPlatformChecks,
  getBackendChecks,
  runChecks,
  hasRequiredFailures,
};
