#!/usr/bin/env node

// =============================================================================
// Memento - Interactive Environment Setup Wizard
// =============================================================================
// Zero external dependencies. Uses only Node.js built-ins.
// Usage: npm run setup:env
// =============================================================================

const readline = require('readline');
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const { execSync } = require('child_process');

// ---------------------------------------------------------------------------
// ANSI helpers
// ---------------------------------------------------------------------------
const R = '\x1b[0m';
const bold = (s) => `\x1b[1m${s}${R}`;
const dim = (s) => `\x1b[2m${s}${R}`;
const red = (s) => `\x1b[31m${s}${R}`;
const green = (s) => `\x1b[32m${s}${R}`;
const yellow = (s) => `\x1b[33m${s}${R}`;
const cyan = (s) => `\x1b[36m${s}${R}`;
const magenta = (s) => `\x1b[35m${s}${R}`;
const blue = (s) => `\x1b[34m${s}${R}`;

// ---------------------------------------------------------------------------
// Readline promisified
// ---------------------------------------------------------------------------
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
});

function question(prompt) {
  return new Promise((resolve) => {
    rl.question(prompt, (answer) => resolve(answer.trim()));
  });
}

// ---------------------------------------------------------------------------
// Crypto helpers
// ---------------------------------------------------------------------------
function generateSecret() {
  return crypto.randomBytes(32).toString('base64');
}

function generatePassword(len = 24) {
  const chars = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz0123456789!@#$%&*';
  const bytes = crypto.randomBytes(len);
  return Array.from(bytes, (b) => chars[b % chars.length]).join('');
}

// ---------------------------------------------------------------------------
// Prisma schema helper
// ---------------------------------------------------------------------------
const PRISMA_SCHEMA_PATH = path.join(__dirname, '..', 'prisma', 'schema.prisma');

function switchPrismaProvider(target) {
  if (!['sqlite', 'postgresql'].includes(target)) return;
  let schema = fs.readFileSync(PRISMA_SCHEMA_PATH, 'utf8');
  schema = schema.replace(
    /(datasource db \{\s*\n\s*provider\s*=\s*)"[^"]+"/,
    `$1"${target}"`
  );
  fs.writeFileSync(PRISMA_SCHEMA_PATH, schema);
}

// ---------------------------------------------------------------------------
// Validation helpers
// ---------------------------------------------------------------------------
function isSensitive(key) {
  const k = key.toUpperCase();
  return k.includes('KEY') || k.includes('SECRET') || k.includes('PASSWORD') || k.includes('PASS');
}

function mask(value) {
  if (!value || value.length <= 8) return '****';
  return value.slice(0, 4) + '****' + value.slice(-4);
}

async function askRequired(prompt, defaultValue) {
  while (true) {
    const answer = await question(prompt);
    const val = answer || defaultValue;
    if (val) return val;
    console.log(red('  This field is required. Please enter a value.'));
  }
}

async function askYesNo(prompt, defaultYes = true) {
  const hint = defaultYes ? 'Y/n' : 'y/N';
  const answer = await question(`${prompt} (${hint}): `);
  if (!answer) return defaultYes;
  return answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes';
}

// ---------------------------------------------------------------------------
// Admin account creation
// ---------------------------------------------------------------------------
async function createLocalAdmin() {
  console.log();
  console.log(cyan(bold('  ── Admin Account ─────────────────────────────────')));
  console.log(dim('  Create the first admin account for your instance.'));
  console.log();

  const email = await askRequired('  Admin email: ', '');
  const name = await question('  Admin name (Admin): ') || 'Admin';
  const password = await askRequired('  Admin password: ', '');

  // Hash password using bcryptjs (installed dependency)
  console.log(dim('  Hashing password...'));
  let hashedPassword;
  try {
    const bcrypt = require('bcryptjs');
    hashedPassword = await bcrypt.hash(password, 12);
  } catch {
    // Fallback: use Node crypto if bcryptjs unavailable
    hashedPassword = crypto.createHash('sha256').update(password).digest('hex');
    console.log(yellow('  Warning: bcryptjs not available, using SHA-256 fallback'));
  }

  // Build inline script that uses Prisma to create the user
  const script = `
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
(async () => {
  try {
    const user = await prisma.user.create({
      data: {
        email: ${JSON.stringify(email.toLowerCase())},
        name: ${JSON.stringify(name)},
        password: ${JSON.stringify(hashedPassword)},
        role: 'ADMIN',
      }
    });
    console.log('OK:' + user.email);
  } catch (e) {
    if (e.code === 'P2002') {
      console.log('EXISTS:${email.toLowerCase()}');
    } else {
      console.error('ERR:' + e.message);
    }
  } finally {
    await prisma.$disconnect();
  }
})();
`;

  // Write temp script and execute
  const tmpFile = path.join(__dirname, '..', '_setup_admin_tmp.js');
  fs.writeFileSync(tmpFile, script, 'utf8');

  try {
    const output = execSync(`node "${tmpFile}"`, {
      cwd: path.join(__dirname, '..'),
      encoding: 'utf8',
      timeout: 30000,
      stdio: ['pipe', 'pipe', 'pipe'],
    }).trim();

    if (output.startsWith('OK:')) {
      console.log(green(`  Admin account created: ${output.slice(3)}`));
      return true;
    } else if (output.startsWith('EXISTS:')) {
      console.log(yellow(`  User ${output.slice(7)} already exists. Skipping.`));
      return false;
    } else if (output.startsWith('ERR:')) {
      console.log(red(`  Error: ${output.slice(4)}`));
      return false;
    }
  } catch (err) {
    console.log(red(`  Failed to create admin: ${err.message}`));
    console.log(dim('  You can create it later with: node scripts/promote-admin.js'));
    return false;
  } finally {
    fs.unlinkSync(tmpFile);
  }
}

// ---------------------------------------------------------------------------
// Write config to SystemConfig DB table
// ---------------------------------------------------------------------------
function writeConfigToDb(vars) {
  // Keys that the admin panel reads from SystemConfig (not from .env)
  const dbKeys = [
    'AI_PROVIDER', 'AI_PROVIDER_TAGS', 'AI_PROVIDER_EMBEDDING', 'AI_PROVIDER_CHAT',
    'AI_MODEL_TAGS', 'AI_MODEL_EMBEDDING', 'AI_MODEL_CHAT',
    'OPENAI_API_KEY', 'OLLAMA_BASE_URL',
    'CUSTOM_OPENAI_API_KEY', 'CUSTOM_OPENAI_BASE_URL',
    'RESEND_API_KEY',
    'SMTP_HOST', 'SMTP_PORT', 'SMTP_USER', 'SMTP_PASS', 'SMTP_FROM',
    'SMTP_SECURE', 'SMTP_IGNORE_CERT',
    'ALLOW_REGISTRATION',
    'MCP_SERVER_MODE', 'MCP_SERVER_URL',
  ];

  const entries = [];
  for (const key of dbKeys) {
    if (vars[key]) {
      entries.push({ key, value: vars[key] });
    }
  }

  if (entries.length === 0) return;

  // Build inline script using Prisma to upsert SystemConfig
  const entriesJson = JSON.stringify(entries);
  const script = `
const { PrismaClient } = require('@prisma/client');
const prisma = new PrismaClient();
(async () => {
  try {
    const entries = ${entriesJson};
    for (const e of entries) {
      await prisma.systemConfig.upsert({
        where: { key: e.key },
        update: { value: e.value },
        create: { key: e.key, value: e.value },
      });
    }
    console.log('OK:' + entries.length);
  } catch (e) {
    console.error('ERR:' + e.message);
  } finally {
    await prisma.$disconnect();
  }
})();
`;

  const tmpFile = path.join(__dirname, '..', '_setup_config_tmp.js');
  fs.writeFileSync(tmpFile, script, 'utf8');

  try {
    const output = execSync(`node "${tmpFile}"`, {
      cwd: path.join(__dirname, '..'),
      encoding: 'utf8',
      timeout: 30000,
      stdio: ['pipe', 'pipe', 'pipe'],
    }).trim();

    if (output.startsWith('OK:')) {
      console.log(green(`  Wrote ${output.slice(3)} settings to database`));
      return true;
    } else if (output.startsWith('ERR:')) {
      console.log(yellow(`  Warning: could not write config to DB: ${output.slice(4)}`));
      return false;
    }
  } catch (err) {
    console.log(yellow('  Warning: could not write config to database.'));
    console.log(dim('  Settings will still work via .env but won\'t appear in the admin panel.'));
    return false;
  } finally {
    fs.unlinkSync(tmpFile);
  }
}

// ---------------------------------------------------------------------------
// Banner
// ---------------------------------------------------------------------------
function showBanner() {
  console.log();
  console.log(cyan(bold('  ╔══════════════════════════════════════════╗')));
  console.log(cyan(bold('  ║     Memento Environment Setup Wizard     ║')));
  console.log(cyan(bold('  ╚══════════════════════════════════════════╝')));
  console.log();
  console.log(dim('  This wizard will help you create environment'));
  console.log(dim('  configuration files for your Memento instance.'));
  console.log();
}

// ---------------------------------------------------------------------------
// Environment choice
// ---------------------------------------------------------------------------
async function chooseEnvironment() {
  console.log(bold('  Which environment do you want to configure?'));
  console.log();
  console.log(`    ${green('1')} - Local development  ${dim('(creates .env in memento-note/)')}`);
  console.log(`    ${green('2')} - Docker deployment   ${dim('(creates .env.docker in Momento/)')}`);
  console.log(`    ${green('3')} - Both                ${dim('(creates both files, shared secret)')}`);
  console.log();

  while (true) {
    const choice = await question('  Select [1/2/3]: ');
    if (choice === '1') return 'local';
    if (choice === '2') return 'docker';
    if (choice === '3') return 'both';
    console.log(yellow('  Please enter 1, 2, or 3.'));
  }
}

// ---------------------------------------------------------------------------
// AI provider menus
// ---------------------------------------------------------------------------
const AI_PROVIDERS = ['openai', 'ollama', 'deepseek', 'openrouter', 'custom-openai'];

function showAiProviderMenu(label) {
  console.log();
  console.log(bold(`  AI Provider for ${label}`));
  console.log(`    ${green('1')} - OpenAI`);
  console.log(`    ${green('2')} - Ollama (local)`);
  console.log(`    ${green('3')} - DeepSeek`);
  console.log(`    ${green('4')} - OpenRouter`);
  console.log(`    ${green('5')} - Custom OpenAI-compatible`);
  console.log(`    ${green('6')} - Skip (configure later)`);
}

async function chooseAiProvider(label) {
  showAiProviderMenu(label);
  while (true) {
    const choice = await question('  Select [1-6]: ');
    if (choice === '1') return 'openai';
    if (choice === '2') return 'ollama';
    if (choice === '3') return 'deepseek';
    if (choice === '4') return 'openrouter';
    if (choice === '5') return 'custom-openai';
    if (choice === '6') return null;
    console.log(yellow('  Please enter a number from 1 to 6.'));
  }
}

async function collectAiProviderFields(provider, defaults = {}) {
  const vars = {};

  switch (provider) {
    case 'openai':
      vars.OPENAI_API_KEY = await askRequired(
        `  OpenAI API Key ${dim(`[${mask(defaults.OPENAI_API_KEY || '')}]`)}: `,
        defaults.OPENAI_API_KEY || ''
      );
      break;

    case 'ollama': {
      const ollamaUrl = defaults.OLLAMA_BASE_URL || 'http://localhost:11434';
      vars.OLLAMA_BASE_URL = await question(`  Ollama Base URL (${ollamaUrl}): `) || ollamaUrl;
      break;
    }

    case 'deepseek': {
      const dsKey = await askRequired(
        `  DeepSeek API Key ${dim(`[${mask(defaults.CUSTOM_OPENAI_API_KEY || '')}]`)}: `,
        defaults.CUSTOM_OPENAI_API_KEY || ''
      );
      vars.CUSTOM_OPENAI_API_KEY = dsKey;
      vars.CUSTOM_OPENAI_BASE_URL = 'https://api.deepseek.com/v1';
      console.log(dim('  Base URL: https://api.deepseek.com/v1'));
      break;
    }

    case 'openrouter': {
      const orKey = await askRequired(
        `  OpenRouter API Key ${dim(`[${mask(defaults.CUSTOM_OPENAI_API_KEY || '')}]`)}: `,
        defaults.CUSTOM_OPENAI_API_KEY || ''
      );
      vars.CUSTOM_OPENAI_API_KEY = orKey;
      vars.CUSTOM_OPENAI_BASE_URL = 'https://openrouter.ai/api/v1';
      console.log(dim('  Base URL: https://openrouter.ai/api/v1'));
      break;
    }

    case 'custom-openai': {
      vars.CUSTOM_OPENAI_API_KEY = await askRequired(
        `  Custom API Key ${dim(`[${mask(defaults.CUSTOM_OPENAI_API_KEY || '')}]`)}: `,
        defaults.CUSTOM_OPENAI_API_KEY || ''
      );
      const defaultUrl = defaults.CUSTOM_OPENAI_BASE_URL || '';
      vars.CUSTOM_OPENAI_BASE_URL = await askRequired(
        `  Custom Base URL ${defaultUrl ? dim(`[${defaultUrl}]`) : ''}: `,
        defaultUrl
      );
      break;
    }
  }

  return vars;
}

async function collectAiModels(provider, feature) {
  const vars = {};
  const defaults = {
    chat: { openai: 'gpt-4o-mini', ollama: 'llama3.2', 'custom-openai': 'gpt-4o-mini', deepseek: 'deepseek-chat', openrouter: 'gpt-4o-mini' },
    tags: { openai: 'gpt-4o-mini', ollama: 'llama3.2', 'custom-openai': 'gpt-4o-mini', deepseek: 'deepseek-chat', openrouter: 'gpt-4o-mini' },
    embedding: { openai: 'text-embedding-3-small', ollama: 'nomic-embed-text', 'custom-openai': 'text-embedding-3-small', deepseek: 'deepseek-chat', openrouter: 'text-embedding-3-small' },
  };

  const modelKey = `AI_MODEL_${feature.toUpperCase()}`;
  const defaultModel = defaults[feature.toLowerCase()]?.[provider] || '';

  if (feature.toLowerCase() === 'chat') {
    const answer = await question(`  Chat model (${defaultModel}): `);
    vars[modelKey] = answer || defaultModel;
  } else if (feature.toLowerCase() === 'tags') {
    const answer = await question(`  Tags model (${defaultModel}): `);
    vars[modelKey] = answer || defaultModel;
  } else if (feature.toLowerCase() === 'embedding') {
    const answer = await question(`  Embedding model (${defaultModel}): `);
    vars[modelKey] = answer || defaultModel;
  }

  return vars;
}

// ---------------------------------------------------------------------------
// Local .env section
// ---------------------------------------------------------------------------
async function collectLocalEnv(sharedSecret) {
  console.log();
  console.log(cyan(bold('  ── Local Development (.env) ──────────────────────')));
  console.log();

  const vars = {};

  // Database type
  console.log(bold('  Database engine'));
  console.log(`    ${green('1')} - PostgreSQL ${dim('(recommended, required for Docker, embeddings)')}`);
  console.log(`    ${green('2')} - SQLite    ${dim('(simple, file-based, no server needed)')}`);
  console.log();
  let dbType;
  while (true) {
    const dbChoice = await question('  Select [1/2]: ');
    if (dbChoice === '1') { dbType = 'postgresql'; break; }
    if (dbChoice === '2') { dbType = 'sqlite'; break; }
    console.log(yellow('  Please enter 1 or 2.'));
  }

  if (dbType === 'sqlite') {
    vars.DATABASE_URL = 'file:./dev.db';
    vars._dbType = 'sqlite';
    console.log(dim('  DATABASE_URL set to file:./dev.db'));
  } else {
    console.log(dim('  If using Docker PostgreSQL: postgresql://memento:memento@localhost:5432/memento'));
    vars.DATABASE_URL = await question(
      `  DATABASE_URL (${dim('postgresql://memento:memento@localhost:5432/memento')}): `
    ) || 'postgresql://memento:memento@localhost:5432/memento';
    vars._dbType = 'postgresql';
  }

  // NEXTAUTH_URL
  vars.NEXTAUTH_URL = await question(
    `  NEXTAUTH_URL (${dim('http://localhost:3000')}): `
  ) || 'http://localhost:3000';

  // NEXTAUTH_SECRET
  const autoSecret = sharedSecret || generateSecret();
  console.log(dim(`  Generated NEXTAUTH_SECRET: ${mask(autoSecret)}`));
  const secretAnswer = await question(`  NEXTAUTH_SECRET (press Enter to use generated): `);
  vars.NEXTAUTH_SECRET = secretAnswer || autoSecret;

  // ALLOW_REGISTRATION
  vars.ALLOW_REGISTRATION = (await askYesNo('  Allow registration?', true)) ? 'true' : 'false';

  // AI Provider (single provider for all features in local)
  const provider = await chooseAiProvider('all features');
  if (provider) {
    vars.AI_PROVIDER = provider;
    const providerVars = await collectAiProviderFields(provider, {});
    Object.assign(vars, providerVars);

    // Ask for model overrides
    const configureModels = await askYesNo('  Configure model names?', false);
    if (configureModels) {
      const chatModels = await collectAiModels(provider, 'chat');
      Object.assign(vars, chatModels);
      const tagModels = await collectAiModels(provider, 'tags');
      Object.assign(vars, tagModels);
      const embModels = await collectAiModels(provider, 'embedding');
      Object.assign(vars, embModels);
    }
  }

  // Email
  const wantEmail = await askYesNo('  Configure email?', false);
  if (wantEmail) {
    const useResend = await askYesNo('  Use Resend? (vs SMTP)', false);
    if (useResend) {
      vars.RESEND_API_KEY = await askRequired('  RESEND_API_KEY: ', '');
    } else {
      vars.SMTP_HOST = await askRequired('  SMTP_HOST: ', '');
      vars.SMTP_PORT = await question(`  SMTP_PORT (${dim('587')}): `) || '587';
      vars.SMTP_USER = await askRequired('  SMTP_USER: ', '');
      vars.SMTP_PASS = await askRequired('  SMTP_PASS: ', '');
      vars.SMTP_FROM = await askRequired('  SMTP_FROM: ', '');
      const secure = await askYesNo('  Use TLS/SSL?', true);
      vars.SMTP_SECURE = secure ? 'true' : 'false';
    }
  }

  // MCP
  const wantMcp = await askYesNo('  Configure MCP server?', false);
  if (wantMcp) {
    const useSse = await askYesNo('  MCP mode: SSE? (vs stdio)', false);
    vars.MCP_SERVER_MODE = useSse ? 'sse' : 'stdio';
    if (useSse) {
      vars.MCP_SERVER_URL = await question(`  MCP Server URL (${dim('http://localhost:3001')}): `) || 'http://localhost:3001';
    }
  }

  return vars;
}

// ---------------------------------------------------------------------------
// Docker .env.docker section
// ---------------------------------------------------------------------------
async function collectDockerEnv(sharedSecret) {
  console.log();
  console.log(cyan(bold('  ── Docker Deployment (.env.docker) ────────────────')));
  console.log();

  const vars = {};

  // NEXTAUTH_URL
  console.log(dim('  Hint: Use your server IP or domain (e.g. http://192.168.1.100:3000)'));
  vars.NEXTAUTH_URL = await question(
    `  NEXTAUTH_URL (${dim('http://localhost:3000')}): `
  ) || 'http://localhost:3000';

  // NEXTAUTH_SECRET
  const autoSecret = sharedSecret || generateSecret();
  console.log(dim(`  Generated NEXTAUTH_SECRET: ${mask(autoSecret)}`));
  const secretAnswer = await question(`  NEXTAUTH_SECRET (press Enter to use generated): `);
  vars.NEXTAUTH_SECRET = secretAnswer || autoSecret;

  // ALLOW_REGISTRATION
  vars.ALLOW_REGISTRATION = (await askYesNo('  Allow registration?', true)) ? 'true' : 'false';

  // PostgreSQL
  console.log();
  console.log(bold('  PostgreSQL Configuration'));
  console.log(dim('  Password auto-generated. You can override it.'));
  const pgPassword = generatePassword();
  console.log(dim(`  Generated POSTGRES_PASSWORD: ${mask(pgPassword)}`));
  vars.POSTGRES_PORT = await question(`  POSTGRES_PORT (${dim('5432')}): `) || '5432';
  vars.POSTGRES_DB = await question(`  POSTGRES_DB (${dim('memento')}): `) || 'memento';
  vars.POSTGRES_USER = await question(`  POSTGRES_USER (${dim('memento')}): `) || 'memento';
  const pgPassAnswer = await question(`  POSTGRES_PASSWORD (press Enter to use generated): `);
  vars.POSTGRES_PASSWORD = pgPassAnswer || pgPassword;

  // AI per-feature
  console.log();
  console.log(bold('  AI Provider Configuration (per-feature)'));

  const tagsProvider = await chooseAiProvider('Tags generation');
  if (tagsProvider) {
    vars.AI_PROVIDER_TAGS = tagsProvider;
    const fields = await collectAiProviderFields(tagsProvider, { OLLAMA_BASE_URL: 'http://ollama:11434' });
    Object.assign(vars, fields);
    const models = await collectAiModels(tagsProvider, 'tags');
    Object.assign(vars, models);
  }

  const embProvider = await chooseAiProvider('Embeddings');
  if (embProvider) {
    vars.AI_PROVIDER_EMBEDDING = embProvider;
    if (!vars.CUSTOM_OPENAI_API_KEY && !vars.OPENAI_API_KEY) {
      const fields = await collectAiProviderFields(embProvider, { OLLAMA_BASE_URL: 'http://ollama:11434' });
      Object.assign(vars, fields);
    }
    const models = await collectAiModels(embProvider, 'embedding');
    Object.assign(vars, models);
  }

  const wantChat = await askYesNo('  Configure a separate chat provider?', false);
  if (wantChat) {
    const chatProvider = await chooseAiProvider('Chat');
    if (chatProvider) {
      vars.AI_PROVIDER_CHAT = chatProvider;
      if (!vars.CUSTOM_OPENAI_API_KEY && !vars.OPENAI_API_KEY) {
        const fields = await collectAiProviderFields(chatProvider, { OLLAMA_BASE_URL: 'http://ollama:11434' });
        Object.assign(vars, fields);
      }
      const models = await collectAiModels(chatProvider, 'chat');
      Object.assign(vars, models);
    }
  }

  // MCP
  const wantMcp = await askYesNo('  Configure MCP server?', false);
  if (wantMcp) {
    const useSse = await askYesNo('  MCP mode: SSE? (vs stdio)', false);
    vars.MCP_MODE = useSse ? 'sse' : 'stdio';
    vars.MCP_PORT = await question(`  MCP_PORT (${dim('3001')}): `) || '3001';
    if (useSse) {
      vars.MCP_SERVER_URL = await question(`  MCP_SERVER_URL (${dim('http://localhost:3001')}): `) || 'http://localhost:3001';
    }
  }

  // Email
  const wantEmail = await askYesNo('  Configure email?', false);
  if (wantEmail) {
    const useResend = await askYesNo('  Use Resend? (vs SMTP)', false);
    if (useResend) {
      vars.RESEND_API_KEY = await askRequired('  RESEND_API_KEY: ', '');
    } else {
      vars.SMTP_HOST = await askRequired('  SMTP_HOST: ', '');
      vars.SMTP_PORT = await question(`  SMTP_PORT (${dim('587')}): `) || '587';
      vars.SMTP_USER = await askRequired('  SMTP_USER: ', '');
      vars.SMTP_PASS = await askRequired('  SMTP_PASS: ', '');
      vars.SMTP_FROM = await askRequired('  SMTP_FROM: ', '');
    }
  }

  return vars;
}

// ---------------------------------------------------------------------------
// File writing
// ---------------------------------------------------------------------------
function generateLocalEnvContent(vars) {
  let content = `# =============================================================================
# Memento Note - Local Environment Configuration
# Generated by setup-env wizard
# =============================================================================

# -----------------------------------------------------------------------------
# Core
# -----------------------------------------------------------------------------
DATABASE_URL="${vars.DATABASE_URL}"
NEXTAUTH_SECRET="${vars.NEXTAUTH_SECRET}"
NEXTAUTH_URL="${vars.NEXTAUTH_URL}"
`;

  if (vars.ALLOW_REGISTRATION) {
    content += `
# -----------------------------------------------------------------------------
# Registration
# -----------------------------------------------------------------------------
ALLOW_REGISTRATION="${vars.ALLOW_REGISTRATION}"
`;
  }

  // AI provider section
  const aiKeys = ['AI_PROVIDER', 'AI_PROVIDER_CHAT', 'AI_PROVIDER_TAGS', 'AI_PROVIDER_EMBEDDING',
    'AI_MODEL_CHAT', 'AI_MODEL_TAGS', 'AI_MODEL_EMBEDDING',
    'OPENAI_API_KEY', 'OLLAMA_BASE_URL', 'CUSTOM_OPENAI_API_KEY', 'CUSTOM_OPENAI_BASE_URL'];
  const hasAi = aiKeys.some((k) => vars[k]);

  if (hasAi) {
    content += `
# -----------------------------------------------------------------------------
# AI Providers
# -----------------------------------------------------------------------------
`;
    if (vars.AI_PROVIDER) content += `AI_PROVIDER="${vars.AI_PROVIDER}"\n`;
    if (vars.AI_PROVIDER_CHAT) content += `AI_PROVIDER_CHAT="${vars.AI_PROVIDER_CHAT}"\n`;
    if (vars.AI_PROVIDER_TAGS) content += `AI_PROVIDER_TAGS="${vars.AI_PROVIDER_TAGS}"\n`;
    if (vars.AI_PROVIDER_EMBEDDING) content += `AI_PROVIDER_EMBEDDING="${vars.AI_PROVIDER_EMBEDDING}"\n`;
    if (vars.AI_MODEL_CHAT) content += `AI_MODEL_CHAT="${vars.AI_MODEL_CHAT}"\n`;
    if (vars.AI_MODEL_TAGS) content += `AI_MODEL_TAGS="${vars.AI_MODEL_TAGS}"\n`;
    if (vars.AI_MODEL_EMBEDDING) content += `AI_MODEL_EMBEDDING="${vars.AI_MODEL_EMBEDDING}"\n`;
    if (vars.OPENAI_API_KEY) content += `OPENAI_API_KEY="${vars.OPENAI_API_KEY}"\n`;
    if (vars.OLLAMA_BASE_URL) content += `OLLAMA_BASE_URL="${vars.OLLAMA_BASE_URL}"\n`;
    if (vars.CUSTOM_OPENAI_API_KEY) content += `CUSTOM_OPENAI_API_KEY="${vars.CUSTOM_OPENAI_API_KEY}"\n`;
    if (vars.CUSTOM_OPENAI_BASE_URL) content += `CUSTOM_OPENAI_BASE_URL="${vars.CUSTOM_OPENAI_BASE_URL}"\n`;
  }

  // Email section
  const emailKeys = ['RESEND_API_KEY', 'SMTP_HOST', 'SMTP_PORT', 'SMTP_USER', 'SMTP_PASS', 'SMTP_FROM', 'SMTP_SECURE', 'SMTP_IGNORE_CERT'];
  const hasEmail = emailKeys.some((k) => vars[k]);

  if (hasEmail) {
    content += `
# -----------------------------------------------------------------------------
# Email
# -----------------------------------------------------------------------------
`;
    if (vars.RESEND_API_KEY) content += `RESEND_API_KEY="${vars.RESEND_API_KEY}"\n`;
    if (vars.SMTP_HOST) content += `SMTP_HOST="${vars.SMTP_HOST}"\n`;
    if (vars.SMTP_PORT) content += `SMTP_PORT="${vars.SMTP_PORT}"\n`;
    if (vars.SMTP_USER) content += `SMTP_USER="${vars.SMTP_USER}"\n`;
    if (vars.SMTP_PASS) content += `SMTP_PASS="${vars.SMTP_PASS}"\n`;
    if (vars.SMTP_FROM) content += `SMTP_FROM="${vars.SMTP_FROM}"\n`;
    if (vars.SMTP_SECURE) content += `SMTP_SECURE="${vars.SMTP_SECURE}"\n`;
  }

  // MCP section
  if (vars.MCP_SERVER_MODE) {
    content += `
# -----------------------------------------------------------------------------
# MCP
# -----------------------------------------------------------------------------
MCP_SERVER_MODE="${vars.MCP_SERVER_MODE}"
`;
    if (vars.MCP_SERVER_URL) content += `MCP_SERVER_URL="${vars.MCP_SERVER_URL}"\n`;
  }

  content += '\n';
  return content;
}

function generateDockerEnvContent(vars) {
  let content = `# =============================================================================
# Memento - Docker Environment Configuration
# Generated by setup-env wizard
# =============================================================================

# =============================================================================
# APPLICATION
# =============================================================================
NEXTAUTH_URL="${vars.NEXTAUTH_URL}"
NEXTAUTH_SECRET="${vars.NEXTAUTH_SECRET}"
`;

  if (vars.ALLOW_REGISTRATION) {
    content += `ALLOW_REGISTRATION="${vars.ALLOW_REGISTRATION}"\n`;
  }

  content += `
# =============================================================================
# POSTGRESQL
# =============================================================================
POSTGRES_PORT=${vars.POSTGRES_PORT || 5432}
POSTGRES_DB=${vars.POSTGRES_DB || 'memento'}
POSTGRES_USER=${vars.POSTGRES_USER || 'memento'}
POSTGRES_PASSWORD=${vars.POSTGRES_PASSWORD || 'memento'}
`;

  // AI per-feature
  const aiKeys = ['AI_PROVIDER_TAGS', 'AI_PROVIDER_EMBEDDING', 'AI_PROVIDER_CHAT',
    'AI_MODEL_TAGS', 'AI_MODEL_EMBEDDING', 'AI_MODEL_CHAT',
    'OLLAMA_BASE_URL', 'OPENAI_API_KEY', 'CUSTOM_OPENAI_API_KEY', 'CUSTOM_OPENAI_BASE_URL'];
  const hasAi = aiKeys.some((k) => vars[k]);

  if (hasAi) {
    content += `
# =============================================================================
# AI PROVIDERS
# =============================================================================
`;
    if (vars.AI_PROVIDER_TAGS) content += `AI_PROVIDER_TAGS=${vars.AI_PROVIDER_TAGS}\n`;
    if (vars.AI_MODEL_TAGS) content += `AI_MODEL_TAGS="${vars.AI_MODEL_TAGS}"\n`;
    if (vars.AI_PROVIDER_EMBEDDING) content += `AI_PROVIDER_EMBEDDING=${vars.AI_PROVIDER_EMBEDDING}\n`;
    if (vars.AI_MODEL_EMBEDDING) content += `AI_MODEL_EMBEDDING="${vars.AI_MODEL_EMBEDDING}"\n`;
    if (vars.AI_PROVIDER_CHAT) content += `AI_PROVIDER_CHAT=${vars.AI_PROVIDER_CHAT}\n`;
    if (vars.AI_MODEL_CHAT) content += `AI_MODEL_CHAT="${vars.AI_MODEL_CHAT}"\n`;
    if (vars.OLLAMA_BASE_URL) content += `OLLAMA_BASE_URL="${vars.OLLAMA_BASE_URL}"\n`;
    if (vars.OPENAI_API_KEY) content += `OPENAI_API_KEY="${vars.OPENAI_API_KEY}"\n`;
    if (vars.CUSTOM_OPENAI_API_KEY) content += `CUSTOM_OPENAI_API_KEY="${vars.CUSTOM_OPENAI_API_KEY}"\n`;
    if (vars.CUSTOM_OPENAI_BASE_URL) content += `CUSTOM_OPENAI_BASE_URL="${vars.CUSTOM_OPENAI_BASE_URL}"\n`;
  }

  // MCP
  if (vars.MCP_MODE || vars.MCP_SERVER_MODE) {
    content += `
# =============================================================================
# MCP SERVER
# =============================================================================
`;
    if (vars.MCP_MODE) content += `MCP_MODE="${vars.MCP_MODE}"\n`;
    if (vars.MCP_PORT) content += `MCP_PORT="${vars.MCP_PORT}"\n`;
    if (vars.MCP_SERVER_URL) content += `MCP_SERVER_URL="${vars.MCP_SERVER_URL}"\n`;
  }

  // Email
  const emailKeys = ['SMTP_HOST', 'SMTP_PORT', 'SMTP_USER', 'SMTP_PASS', 'SMTP_FROM', 'RESEND_API_KEY'];
  const hasEmail = emailKeys.some((k) => vars[k]);

  if (hasEmail) {
    content += `
# =============================================================================
# EMAIL
# =============================================================================
`;
    if (vars.SMTP_HOST) content += `SMTP_HOST="${vars.SMTP_HOST}"\n`;
    if (vars.SMTP_PORT) content += `SMTP_PORT="${vars.SMTP_PORT}"\n`;
    if (vars.SMTP_USER) content += `SMTP_USER="${vars.SMTP_USER}"\n`;
    if (vars.SMTP_PASS) content += `SMTP_PASS="${vars.SMTP_PASS}"\n`;
    if (vars.SMTP_FROM) content += `SMTP_FROM="${vars.SMTP_FROM}"\n`;
    if (vars.RESEND_API_KEY) content += `RESEND_API_KEY="${vars.RESEND_API_KEY}"\n`;
  }

  content += '\n';
  return content;
}

// ---------------------------------------------------------------------------
// File conflict handling
// ---------------------------------------------------------------------------
async function handleExistingFile(filePath) {
  if (!fs.existsSync(filePath)) return 'write';

  console.log(yellow(`\n  File already exists: ${filePath}`));

  while (true) {
    console.log(`    ${green('1')} - Overwrite`);
    console.log(`    ${green('2')} - Skip`);
    console.log(`    ${green('3')} - View current content`);
    const choice = await question('  Choose [1/2/3]: ');

    if (choice === '1') return 'write';
    if (choice === '2') return 'skip';
    if (choice === '3') {
      const content = fs.readFileSync(filePath, 'utf8');
      console.log();
      console.log(dim('  ── Current content ─────────────────────────────'));
      content.split('\n').forEach((line) => console.log(dim(`  ${line}`)));
      console.log(dim('  ─────────────────────────────────────────────────'));
      continue;
    }
    console.log(yellow('  Please enter 1, 2, or 3.'));
  }
}

// ---------------------------------------------------------------------------
// Recap display
// ---------------------------------------------------------------------------
function showRecap(vars, title) {
  console.log();
  console.log(bold(`  ${title}`));
  console.log(dim('  ─────────────────────────────────────────────────'));
  const keys = Object.keys(vars).filter((k) => !k.startsWith('_'));
  const maxLen = Math.max(...keys.map((k) => k.length));
  for (const key of keys) {
    const value = vars[key];
    const display = isSensitive(key) ? mask(value) : value;
    const padded = key.padEnd(maxLen);
    console.log(`  ${cyan(padded)} = ${display}`);
  }
  console.log();
}

// ---------------------------------------------------------------------------
// Next steps
// ---------------------------------------------------------------------------
function showNextSteps(results) {
  console.log();
  console.log(cyan(bold('  ══════════════════════════════════════════════════')));
  console.log(cyan(bold('  Setup complete!')));
  console.log(cyan(bold('  ══════════════════════════════════════════════════')));
  console.log();

  if (results.local) {
    console.log(green(`  Created: ${results.local}`));
  }
  if (results.docker) {
    console.log(green(`  Created: ${results.docker}`));
  }

  console.log();
  console.log(bold('  Next steps:'));

  if (results.local) {
    console.log();
    console.log('  For local development:');
    if (!results.adminCreated) {
      console.log(dim('    npx prisma db push'));
    }
    console.log(dim('    npm run dev'));
    if (!results.adminCreated) {
      console.log(dim('    Then register via /register and run:'));
      console.log(dim('    node scripts/promote-admin.js your@email.com'));
    }
  }

  if (results.docker) {
    console.log();
    console.log('  For Docker deployment:');
    console.log(dim('    cd .. && docker compose up -d'));
    console.log(dim('    Register via /register, then promote to admin:'));
    console.log(dim('    docker compose exec memento-note node scripts/promote-admin.js your@email.com'));
  }

  console.log();
}

// ---------------------------------------------------------------------------
// Main
// ---------------------------------------------------------------------------
async function main() {
  // Handle Ctrl+C gracefully
  rl.on('close', () => {
    console.log();
    console.log(dim('  Wizard cancelled.'));
    process.exit(0);
  });

  showBanner();

  const envChoice = await chooseEnvironment();
  const results = {};
  let sharedSecret = null;

  // For "both" mode, generate one shared secret
  if (envChoice === 'both') {
    sharedSecret = generateSecret();
    console.log(dim(`\n  Shared NEXTAUTH_SECRET: ${mask(sharedSecret)}`));
  }

  // Local section
  if (envChoice === 'local' || envChoice === 'both') {
    const localVars = await collectLocalEnv(sharedSecret);
    const localPath = path.join(__dirname, '..', '.env');

    showRecap(localVars, 'Local .env recap:');
    const action = await handleExistingFile(localPath);

    if (action === 'write') {
      // Extract internal metadata before writing
      const dbType = localVars._dbType || 'postgresql';
      delete localVars._dbType;

      const content = generateLocalEnvContent(localVars);
      fs.writeFileSync(localPath, content, 'utf8');
      results.local = localPath;
      results.dbType = dbType;

      // Switch Prisma schema provider to match chosen database
      try {
        switchPrismaProvider(dbType);
        console.log(green(`  Prisma schema switched to ${dbType}`));
      } catch (err) {
        console.log(yellow(`  Warning: could not update prisma/schema.prisma: ${err.message}`));
      }

      // Push schema to database
      console.log(dim('  Pushing database schema...'));
      let dbReady = false;
      try {
        execSync('npx prisma generate', { cwd: path.join(__dirname, '..'), stdio: 'pipe' });
        execSync('npx prisma db push --accept-data-loss', { cwd: path.join(__dirname, '..'), stdio: 'pipe' });
        console.log(green('  Database schema synced'));
        dbReady = true;
      } catch (err) {
        console.log(yellow('  Warning: could not push schema to database.'));
        console.log(dim(`    ${err.message?.split('\n')[0] || ''}`));
        console.log(dim('  Run "npx prisma db push" manually after ensuring the database is running.'));
      }

      // Write AI/email/MCP config to SystemConfig DB table
      // so the admin panel shows correct values instead of hardcoded defaults
      if (dbReady) {
        console.log(dim('  Writing settings to database...'));
        writeConfigToDb(localVars);
      }

      // Create admin account
      const wantAdmin = await askYesNo('\n  Create an admin account now?', true);
      if (wantAdmin) {
        await createLocalAdmin();
        results.adminCreated = true;
      }

      // Capture shared secret for docker section
      if (envChoice === 'both') {
        sharedSecret = localVars.NEXTAUTH_SECRET;
      }
    } else {
      console.log(yellow('  Skipped .env'));
      if (envChoice === 'both') {
        sharedSecret = sharedSecret; // keep generated one
      }
    }
  }

  // Docker section
  if (envChoice === 'docker' || envChoice === 'both') {
    // Verify docker-compose.yml exists in parent
    const parentDir = path.resolve(__dirname, '..', '..');
    const dockerComposePath = path.join(parentDir, 'docker-compose.yml');

    if (!fs.existsSync(dockerComposePath)) {
      console.log(red(`\n  Error: docker-compose.yml not found at ${parentDir}`));
      console.log(red('  Make sure you are running this from the memento-note directory inside the Momento project.'));
      process.exit(1);
    }

    const dockerVars = await collectDockerEnv(sharedSecret);
    const dockerEnvPath = path.join(parentDir, '.env.docker');

    showRecap(dockerVars, 'Docker .env.docker recap:');
    const action = await handleExistingFile(dockerEnvPath);

    if (action === 'write') {
      const content = generateDockerEnvContent(dockerVars);
      fs.writeFileSync(dockerEnvPath, content, 'utf8');
      results.docker = dockerEnvPath;
    } else {
      console.log(yellow('  Skipped .env.docker'));
    }
  }

  showNextSteps(results);
  rl.close();
}

main().catch((err) => {
  console.error(red(`\n  Unexpected error: ${err.message}`));
  rl.close();
  process.exit(1);
});
