#!/usr/bin/env node

/**
 * Unified release script — builds everything and deploys to Cloudflare.
 *
 * Usage:
 *   node scripts/release.mjs                # full release: build all + deploy
 *   node scripts/release.mjs --build-only   # build everything, skip deploy
 *   node scripts/release.mjs --deploy-only  # deploy only (assumes prior build)
 *   node scripts/release.mjs --secrets      # set Cloudflare secrets interactively
 *   node scripts/release.mjs --r2-setup     # create R2 bucket + bind to Pages
 *
 * Steps:
 *   1. Build macOS Electron app (DMG + ZIP for arm64 + x64)
 *   2. Build Windows Electron app (NSIS installer + portable)
 *   3. Build distributable ZIP (browser version)
 *   4. Build static site (includes _worker.js for Cloudflare)
 *   5. Upload Electron artifacts to R2 (large files > 25 MiB)
 *   6. Deploy site/ to Cloudflare Pages
 *
 * First-time setup:
 *   node scripts/release.mjs --secrets
 *   node scripts/release.mjs --r2-setup
 */

import { execSync } from 'child_process';
import { existsSync, readFileSync, readdirSync, statSync } from 'fs';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { createInterface } from 'readline';

const __dirname = dirname(fileURLToPath(import.meta.url));
const ROOT = join(__dirname, '..');
const DIST_DIR = join(ROOT, 'dist');

// Parse CLI args
const args = process.argv.slice(2);
const buildOnly = args.includes('--build-only');
const deployOnly = args.includes('--deploy-only');
const secretsOnly = args.includes('--secrets');
const r2Setup = args.includes('--r2-setup');
const skipElectron = args.includes('--skip-electron');
const skipZip = args.includes('--skip-zip');

// Load version
let version = '5.1.0';
try {
  version = JSON.parse(readFileSync(join(ROOT, 'version.json'), 'utf-8')).version;
} catch {}

// Cloudflare config
const projectName = 'ai-do-site';
const r2BucketName = 'ai-do-releases';

function run(cmd, opts = {}) {
  console.log(`  $ ${cmd}`);
  execSync(cmd, { cwd: ROOT, stdio: 'inherit', ...opts });
}

function runQuiet(cmd, opts = {}) {
  return execSync(cmd, { cwd: ROOT, encoding: 'utf-8', ...opts }).trim();
}

function step(num, label) {
  console.log(`\n${'='.repeat(60)}`);
  console.log(`  Step ${num}: ${label}`);
  console.log('='.repeat(60));
}

function ask(question) {
  const rl = createInterface({ input: process.stdin, output: process.stdout });
  return new Promise(resolve => {
    rl.question(question, answer => {
      rl.close();
      resolve(answer.trim());
    });
  });
}

// ── Secrets management ──────────────────────────────────────

async function setSecrets() {
  console.log('\n[release] Setting Cloudflare Pages secrets...\n');

  // Try to load from .env first
  const envPath = join(ROOT, '.env');
  const envVars = {};
  if (existsSync(envPath)) {
    const envContent = readFileSync(envPath, 'utf-8');
    for (const line of envContent.split('\n')) {
      const match = line.match(/^([A-Z_]+)\s*=\s*(.+)$/);
      if (match) envVars[match[1]] = match[2].trim().replace(/^['"]|['"]$/g, '');
    }
    console.log(`  Found .env with keys: ${Object.keys(envVars).join(', ')}`);
  }

  const secrets = [
    { name: 'APP_TOKEN', envKey: 'APP_TOKEN', desc: 'Shared auth token (must match config.mjs DEFAULTS.worker.token)' },
    { name: 'ANTHROPIC_API_KEY', envKey: 'ANTHROPIC_API_KEY', desc: 'Anthropic API key for LLM proxy' },
    { name: 'XAI_API_KEY', envKey: 'XAI_API_KEY', desc: 'xAI API key for Grok voice proxy' },
    { name: 'OPENAI_API_KEY', envKey: 'OPENAI_API_KEY', desc: 'OpenAI API key for voice proxy (optional)' },
  ];

  for (const secret of secrets) {
    const fromEnv = envVars[secret.envKey];
    let value = fromEnv;

    if (fromEnv) {
      const use = await ask(`  ${secret.name}: found in .env — use it? (Y/n) `);
      if (use.toLowerCase() === 'n') value = null;
    }

    if (!value) {
      value = await ask(`  ${secret.name} (${secret.desc}): `);
    }

    if (!value) {
      console.log(`    Skipped ${secret.name}`);
      continue;
    }

    try {
      // Use echo to pipe the value to wrangler
      execSync(
        `echo "${value}" | wrangler pages secret put ${secret.name} --project-name ${projectName}`,
        { cwd: ROOT, stdio: ['pipe', 'pipe', 'inherit'] }
      );
      console.log(`    Set ${secret.name}`);
    } catch (err) {
      console.error(`    Failed to set ${secret.name}: ${err.message}`);
    }
  }

  console.log('\n[release] Secrets configured!\n');
  console.log('  Remaining setup:');
  console.log('    1. Create KV:  wrangler kv namespace create RATE_LIMITS');
  console.log(`    2. Create R2:  node scripts/release.mjs --r2-setup`);
  console.log(`    3. Bind both in Cloudflare dashboard:`);
  console.log(`       Pages > ${projectName} > Settings > Bindings`);
  console.log(`       - KV Namespace: RATE_LIMITS`);
  console.log(`       - R2 Bucket: RELEASES → ${r2BucketName}`);
}

// ── R2 setup ────────────────────────────────────────────────

async function setupR2() {
  console.log('\n[release] Setting up R2 bucket for release files...\n');

  try {
    run(`wrangler r2 bucket create ${r2BucketName}`);
    console.log(`\n  R2 bucket "${r2BucketName}" created!`);
  } catch (err) {
    // Bucket may already exist
    if (err.message?.includes('already exists') || err.status === 409) {
      console.log(`  R2 bucket "${r2BucketName}" already exists.`);
    } else {
      console.error(`  Failed to create R2 bucket:`, err.message);
      console.error('  You may need to create it manually in the Cloudflare dashboard.');
    }
  }

  console.log('\n  Next step: bind R2 bucket to your Pages project:');
  console.log(`    Cloudflare Dashboard > Pages > ${projectName} > Settings > Bindings`);
  console.log(`    Add R2 Bucket Binding:`);
  console.log(`      Variable name: RELEASES`);
  console.log(`      Bucket: ${r2BucketName}`);
}

// ── Build steps ─────────────────────────────────────────────

function buildElectronMac() {
  step(1, `Build macOS Electron app (v${version})`);
  try {
    run('npx electron-builder --mac --publish never');
    console.log('\n  macOS build complete!');
  } catch (err) {
    console.error('\n  macOS build failed:', err.message);
    console.error('  (This is expected on non-macOS systems)');
  }
}

function buildElectronWin() {
  step(2, `Build Windows Electron app (v${version})`);
  try {
    run('npx electron-builder --win --publish never');
    console.log('\n  Windows build complete!');
  } catch (err) {
    console.error('\n  Windows build failed:', err.message);
    console.error('  (Cross-compilation may require Wine on macOS/Linux)');
  }
}

function buildZip() {
  step(3, `Build distributable ZIP (v${version})`);
  run('bash scripts/build-zip.sh');
}

function buildSite() {
  step(4, `Build static site (v${version})`);
  run('node scripts/build-site.mjs');
}

function uploadReleasesToR2() {
  step(5, `Upload Electron artifacts to R2 (${r2BucketName})`);

  if (!existsSync(DIST_DIR)) {
    console.log('  No dist/ directory found — skipping R2 upload.');
    return;
  }

  let uploaded = 0;

  // Scan dist/ for release artifacts
  const distFiles = readdirSync(DIST_DIR);
  for (const file of distFiles) {
    // Upload DMG, EXE, and Electron ZIP files
    if (file.endsWith('.dmg') || file.endsWith('.exe') || file.endsWith('.zip')) {
      // Skip mac-universal and other non-release zips
      if (file.endsWith('.zip') && !file.includes('arm64') && !file.includes('x64')) continue;
      // Skip blockmap files
      if (file.includes('blockmap')) continue;
      // Only upload current version (skip old artifacts)
      if (!file.includes(version)) continue;

      const src = join(DIST_DIR, file);
      const size = statSync(src).size;
      const sizeMB = (size / (1024 * 1024)).toFixed(1);
      console.log(`  Uploading: ${file} (${sizeMB} MB)...`);

      try {
        run(`wrangler r2 object put "${r2BucketName}/${file}" --file "${src}" --remote`, { stdio: 'pipe' });
        console.log(`    Uploaded ${file}`);
        uploaded++;
      } catch (err) {
        console.error(`    Failed to upload ${file}: ${err.message}`);
      }
    }
  }

  if (uploaded === 0) {
    console.log('  No release artifacts found in dist/');
  } else {
    console.log(`  Uploaded ${uploaded} artifacts to R2 bucket "${r2BucketName}"`);
    console.log('  Downloads served via: /releases/<filename>');
  }
}

function deploy() {
  step(6, `Deploy to Cloudflare Pages (${projectName})`);
  run('node scripts/deploy.mjs');
}

// ── Main ────────────────────────────────────────────────────

async function main() {
  console.log(`\n[release] AI-Do Agent v${version} — Release Build`);
  console.log(`  Platform: ${process.platform}`);
  console.log(`  Node: ${process.version}`);
  console.log(`  Root: ${ROOT}`);

  // Secrets mode
  if (secretsOnly) {
    await setSecrets();
    return;
  }

  // R2 setup mode
  if (r2Setup) {
    await setupR2();
    return;
  }

  const startTime = Date.now();

  if (!deployOnly) {
    // Build Electron apps
    if (!skipElectron) {
      buildElectronMac();
      buildElectronWin();
    } else {
      console.log('\n  Skipping Electron builds (--skip-electron)');
    }

    // Build ZIP
    if (!skipZip) {
      buildZip();
    } else {
      console.log('\n  Skipping ZIP build (--skip-zip)');
    }

    // Build site
    buildSite();

    // Upload Electron artifacts to R2 (instead of copying to site/)
    if (!skipElectron) {
      uploadReleasesToR2();
    }
  }

  // Deploy
  if (!buildOnly) {
    deploy();
  }

  const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
  console.log(`\n${'='.repeat(60)}`);
  console.log(`  Release complete! (${elapsed}s)`);
  console.log('='.repeat(60));

  if (!buildOnly) {
    console.log(`\n  Site: https://${projectName}.pages.dev`);
    console.log(`  Desktop downloads: https://${projectName}.pages.dev/desktop.html`);
    console.log(`  Browser download: https://${projectName}.pages.dev/download.html`);
  }

  console.log(`\n  First-time setup?`);
  console.log(`    node scripts/release.mjs --secrets   # set API keys`);
  console.log(`    node scripts/release.mjs --r2-setup  # create R2 bucket`);
}

main().catch(err => {
  console.error('\n[release] Fatal error:', err);
  process.exit(1);
});
