'use strict';

/**
 * wizard-runner.js
 * ──────────────────────────────────────────────────────────────
 * 通用 TUI 向导引擎
 *
 * ── 支持的 step 类型 ──────────────────────────────────────────
 *   check       只读查询（execSync，可反复运行）
 *   action      有副作用的操作（execSync）
 *   interactive 需要终端交互（spawnSync + inherit stdio，如扫码）
 *   input       执行前需填写动态参数（collectInputs + fillTemplate）
 *   submenu     进入子向导（栈式导航，q 键返回上一级）
 *
 * ── step 扩展字段 ────────────────────────────────────────────
 *   requires: ["step-id"]   执行前检查前置步骤是否已完成
 *   capture: "var_name"     执行后把输出存入 context.captures
 *   show_capture: "var_name" input 步骤填参前展示指定 capture 作为参考
 *
 * ── 交互快捷键 ───────────────────────────────────────────────
 *   ↑ ↓         上下移动光标
 *   1-9         数字快速跳转到对应步骤
 *   Enter       执行当前步骤
 *   q / Ctrl+C  退出（子向导返回上一级）
 *
 * ── 共享 Context ─────────────────────────────────────────────
 *   父子向导共用同一个 context 对象：
 *     context.captures: { [var_name]: string }  输出捕获
 *
 * ── 栈式导航 ─────────────────────────────────────────────────
 *   顶级向导：q → 退出程序
 *   子向导：opts.onExit 回调父级，键盘控制权归还父级
 */

const path   = require('path');
const chalk  = require('chalk');
const { execSync, spawnSync } = require('child_process');

const { getW, hr, cls, indent, BADGE, renderCommand } = require('./render');
const { collectInputs, fillTemplate }                  = require('./input-prompt');
const { loadConfig }                                   = require('./config-loader');

// ─────────────────────────────────────────────────────────────

class WizardRunner {
  /**
   * @param {Object} config         - wizard config（含 steps[]，已由 config-loader 处理）
   * @param {Object} [opts]
   * @param {Function} [opts.onExit]  - 有值 = 子向导模式，退出时回调父级
   * @param {Object} [opts.context]   - 共享 context（父子向导共用）
   */
  constructor(config, opts = {}) {
    this.config   = config;
    this.steps    = config.steps;
    this.cursor   = 0;
    this._busy    = false;
    this.onExit   = opts.onExit   || null;
    this._handler = null;
    this._resizeHandler = null;

    // 共享 context（父子向导共用同一对象）
    this.context = opts.context || { captures: {} };

    // 初始化每步状态
    this.statuses = {};
    this.steps.forEach(s => {
      if      (s.type === 'check')   this.statuses[s.id] = 'check';
      else if (s.type === 'submenu') this.statuses[s.id] = 'submenu';
      else                           this.statuses[s.id] = 'pending';
    });
  }

  // ── 渲染：主界面 ────────────────────────────────────────────

  render() {
    cls();
    const W = getW();
    const { config, steps, cursor, statuses } = this;
    const isChild = !!this.onExit;

    console.log('');

    if (isChild) {
      console.log(
        '  ' + chalk.dim('← q 返回') + '  ' +
        chalk.bold.cyan('⚡ ' + config.title)
      );
    } else {
      console.log('  ' + chalk.bold.cyan('⚡ ' + config.title));
    }
    console.log('  ' + chalk.gray(config.subtitle || ''));
    console.log('');

    // 步骤列表
    const navHint = isChild
      ? chalk.dim(' ( ↑↓/数字 移动    Enter 执行    q 返回 )')
      : chalk.dim(' ( ↑↓/数字 移动    Enter 执行    q 退出 )');

    console.log('  ' + chalk.bold('步骤列表') + navHint);
    console.log('  ' + chalk.gray(hr('─', W - 4)));

    steps.forEach((step, i) => {
      const sel   = i === cursor;
      const arrow = sel ? chalk.cyan.bold('▶') : ' ';
      const num   = chalk.dim(String(i + 1).padStart(2) + '.');
      const label = sel ? chalk.bold.white(step.label) : chalk.white(step.label);
      const badge = (BADGE[statuses[step.id]] || BADGE.pending)();

      // requires 未满足时显示锁定标记
      const locked = this._isLocked(step);
      const lockMark = locked ? chalk.dim(' 🔒') : '';

      console.log(`  ${arrow} ${num} ${step.icon}  ${label}${lockMark}  ${badge}`);
    });

    console.log('  ' + chalk.gray(hr('─', W - 4)));
    console.log('');

    // 说明区
    const step  = steps[cursor];
    const teach = step.teach;

    console.log('  ' + chalk.bold.yellow('📖 步骤说明'));
    console.log('');
    console.log('  ' + chalk.bold.white('【' + teach.title + '】'));
    console.log('');
    console.log(indent(teach.description, '  '));
    console.log('');

    // 命令预览 or 子向导提示
    if (step.type === 'submenu') {
      console.log('  ' + chalk.magenta('▸  按 Enter 进入子向导'));
    } else if (step.type === 'input') {
      console.log('  ' + chalk.dim('命令模板（参数待填写）：'));
      renderCommand(step.command.template);
    } else if (step.command) {
      console.log('  ' + chalk.dim('执行命令：'));
      renderCommand(step.command.display || step.command.exec);
    }
    console.log('');

    if (teach.look_for) {
      console.log('  ' + chalk.dim('👀 执行后观察：') + chalk.yellow(teach.look_for));
      console.log('');
    }
    if (teach.tip) {
      console.log('  ' + chalk.dim('💡 ') + chalk.gray(teach.tip));
      console.log('');
    }

    // requires 提示（主界面也显示）
    if (this._isLocked(step)) {
      const missing = this._getMissingRequires(step);
      console.log('  ' + chalk.yellow('🔒 建议先完成：'));
      missing.forEach(id => {
        const idx = this.steps.findIndex(s => s.id === id);
        const s   = this.steps[idx];
        console.log('  ' + chalk.dim('   → 步骤 ' + (idx + 1) + '：' + (s ? s.label : id)));
      });
      console.log('');
    }
  }

  // ── 执行步骤（分发） ─────────────────────────────────────────

  async executeStep(idx) {
    const step = this.steps[idx];

    // requires 前置检查
    if (this._isLocked(step)) {
      const ok = await this._showRequiresWarning(step);
      if (!ok) { this.render(); return; }
    }

    switch (step.type) {
      case 'submenu': return this._openSubmenu(step);
      case 'input':   return this._executeInputStep(step);
      default:        return this._executeCommandStep(step);
    }
  }

  // ── requires 相关 ────────────────────────────────────────────

  _isLocked(step) {
    return this._getMissingRequires(step).length > 0;
  }

  _getMissingRequires(step) {
    if (!step.requires || !step.requires.length) return [];
    return step.requires.filter(id => this.statuses[id] !== 'done');
  }

  async _showRequiresWarning(step) {
    const W       = getW();
    const missing = this._getMissingRequires(step);

    cls();
    console.log('');
    console.log('  ' + chalk.bold.yellow('🔒 前置步骤未完成'));
    console.log('  ' + chalk.gray(hr('─', W - 4)));
    console.log('');
    console.log('  建议在执行「' + chalk.bold(step.label) + '」之前先完成：');
    console.log('');

    missing.forEach(id => {
      const idx = this.steps.findIndex(s => s.id === id);
      const s   = this.steps[idx];
      console.log('  ' + chalk.yellow('  → 步骤 ' + (idx + 1) + '：' + (s ? s.label : id)));
    });

    console.log('');
    console.log('  ' + chalk.gray(hr('─', W - 4)));
    console.log(
      '  ' + chalk.dim('按 Enter 仍然继续') +
      chalk.dim('  |  按 q 返回先完成前置步骤')
    );

    return this._waitConfirm();
  }

  // ── submenu：进入子向导 ──────────────────────────────────────

  async _openSubmenu(step) {
    const baseDir    = this.config._dir || __dirname;
    const configPath = path.resolve(baseDir, step.submenu);
    const subConfig  = loadConfig(configPath);

    this._removeKeyboard();

    const sub = new WizardRunner(subConfig, {
      context: this.context,   // 共享同一个 context
      onExit: () => {
        this._setupKeyboard();
        this.render();
      },
    });

    sub.run();
  }

  // ── 普通命令步骤 ─────────────────────────────────────────────

  async _executeCommandStep(step) {
    const W     = getW();
    const teach = step.teach;

    cls();
    console.log('');
    console.log('  ' + chalk.bold.cyan('▶ 执行：' + step.icon + '  ' + step.label));
    console.log('  ' + chalk.gray(hr('─', W - 4)));
    console.log('');
    console.log('  ' + chalk.bold.yellow('📋 执行前说明'));
    console.log('');
    console.log(indent(teach.description, '  '));
    console.log('');
    console.log('  ' + chalk.bold('将要执行的命令：'));
    console.log('');
    renderCommand(step.command.display || step.command.exec);
    console.log('');

    if (step.type === 'interactive') {
      console.log('  ' + chalk.yellow('⚠️  此步骤是交互式操作（如显示二维码），将接管终端'));
      console.log('');
    }

    // capture 提示
    if (step.capture) {
      console.log('  ' + chalk.cyan('📥 执行结果将被记录，供后续步骤参考使用'));
      console.log('');
    }

    if (teach.tip) {
      console.log('  ' + chalk.dim('💡 ') + chalk.gray(teach.tip));
      console.log('');
    }

    console.log('  ' + chalk.gray(hr('─', W - 4)));
    console.log('  ' + chalk.dim('按 Enter 开始执行  |  按 q 取消'));

    const ok = await this._waitConfirm();
    if (!ok) { this.render(); return; }

    await this._runCommand(step, step.command.exec || step.command.display);
  }

  // ── input 步骤：填参数后执行 ─────────────────────────────────

  async _executeInputStep(step) {
    const W     = getW();
    const teach = step.teach;

    // 第一页：说明 + 模板
    cls();
    console.log('');
    console.log('  ' + chalk.bold.cyan('▶ 执行：' + step.icon + '  ' + step.label));
    console.log('  ' + chalk.gray(hr('─', W - 4)));
    console.log('');
    console.log('  ' + chalk.bold.yellow('📋 执行前说明'));
    console.log('');
    console.log(indent(teach.description, '  '));
    console.log('');
    console.log('  ' + chalk.bold('命令模板（需要填写参数）：'));
    console.log('');
    renderCommand(step.command.template);
    console.log('');

    if (teach.tip) {
      console.log('  ' + chalk.dim('💡 ') + chalk.gray(teach.tip));
      console.log('');
    }

    console.log('  ' + chalk.gray(hr('─', W - 4)));
    console.log('  ' + chalk.dim('按 Enter 开始填写参数  |  按 q 取消'));

    const ok = await this._waitConfirm();
    if (!ok) { this.render(); return; }

    // 第二页：参考信息 + 填写参数
    cls();
    console.log('');
    console.log('  ' + chalk.bold.cyan('📝 填写参数：' + step.label));
    console.log('  ' + chalk.gray(hr('─', W - 4)));

    // 如果有 show_capture，展示参考信息（教学重点）
    if (step.show_capture) {
      const captured = this.context.captures[step.show_capture];
      if (captured && captured.trim()) {
        console.log('');
        console.log('  ' + chalk.bold.yellow('📋 参考信息（来自上一步执行结果）：'));
        console.log('  ' + chalk.gray(hr('─', W - 4)));
        // 显示捕获的输出（限制 30 行，避免超出屏幕）
        const lines = captured.split('\n').filter(l => l.trim());
        const shown = lines.slice(0, 30);
        shown.forEach(l => console.log('  ' + chalk.dim(l)));
        if (lines.length > 30) {
          console.log('  ' + chalk.dim('... 输出已截断，共 ' + lines.length + ' 行'));
        }
        console.log('  ' + chalk.gray(hr('─', W - 4)));
      } else {
        console.log('');
        console.log('  ' + chalk.yellow('⚠️  暂无参考信息（建议先执行对应的查看步骤）'));
      }
    }

    console.log('');

    const values    = await collectInputs(step.inputs);
    const actualCmd = fillTemplate(step.command.template, values);

    // 第三页：确认最终命令
    cls();
    console.log('');
    console.log('  ' + chalk.bold.cyan('✅ 确认执行：' + step.icon + '  ' + step.label));
    console.log('  ' + chalk.gray(hr('─', W - 4)));
    console.log('');
    console.log('  ' + chalk.bold('已填入参数，将要执行：'));
    console.log('');
    renderCommand(actualCmd);
    console.log('');
    console.log('  ' + chalk.gray(hr('─', W - 4)));
    console.log(
      '  ' + chalk.dim('按 ') + chalk.bold.green('y') +
      chalk.dim(' 确认执行  |  按其他键取消（防止误触）')
    );

    const finalOk = await this._waitYesConfirm();
    if (!finalOk) { this.render(); return; }

    await this._runCommand({ ...step, type: 'action' }, actualCmd);
  }

  // ── 实际运行命令 ─────────────────────────────────────────────

  async _runCommand(step, cmdStr) {
    const W = getW();
    cls();
    console.log('');
    console.log('  ' + chalk.bold.cyan('⚡ 正在执行：' + step.label));
    console.log('  ' + chalk.dim('$ ' + cmdStr));
    console.log('  ' + chalk.gray(hr('─', W - 4)));
    console.log('');
    console.log('  ' + chalk.dim('↓ 命令输出 ↓'));
    console.log('');

    process.stdin.setRawMode(false);

    let success = false;

    try {
      if (step.capture) {
        // 捕获 stdout，同时透传 stdin/stderr
        const result = spawnSync(cmdStr, {
          shell:  true,
          stdio:  ['inherit', 'pipe', 'inherit'],
          encoding: 'utf8',
        });
        const output = result.stdout || '';
        process.stdout.write(output);                    // 显示给学生看
        this.context.captures[step.capture] = output;   // 同时存起来
        success = result.status === 0;
      } else if (step.type === 'interactive') {
        const r = spawnSync(cmdStr, { shell: true, stdio: 'inherit' });
        success = r.status === 0;
      } else {
        execSync(cmdStr, { shell: true, stdio: 'inherit' });
        success = true;
      }
    } catch { success = false; }

    process.stdin.setRawMode(true);
    process.stdin.resume(); // 命令执行完毕后确保 stdin 处于 flowing 状态
    this.statuses[step.id] = success ? 'done' : 'error';


    // ── 成功后光标自动推进到下一步 ──
    if (success && this.cursor < this.steps.length - 1) {
      this.cursor++;
    }

    console.log('');
    console.log('  ' + chalk.gray(hr('─', W - 4)));

    if (success) {
      console.log('  ' + chalk.bold.green('✓ 执行完成！'));
      if (step.teach?.look_for) {
        console.log('  ' + chalk.yellow('👆 观察上方输出：' + step.teach.look_for));
      }
      if (step.capture) {
        console.log('  ' + chalk.cyan('📥 输出已记录，后续步骤可作为参考'));
      }
      if (this.cursor > 0) {
        const next = this.steps[this.cursor];
        console.log('  ' + chalk.dim('→ 下一步：' + next.icon + ' ' + next.label));
      }
    } else {
      console.log('  ' + chalk.bold.red('✗ 执行遇到问题，请查看上方错误信息'));
    }

    console.log('');
    console.log('  ' + chalk.dim('按任意键返回步骤列表...'));

    await this._waitAnyKey();
    this.render();
  }

  // ── 键盘管理 ─────────────────────────────────────────────────

  _onData(buf) {
    if (this._busy) return;
    const k = buf.toString();

    // 数字快速跳转（1-9）
    const num = parseInt(k, 10);
    if (!isNaN(num) && num >= 1 && num <= this.steps.length) {
      this.cursor = num - 1;
      this.render();
      return;
    }

    if      (k === '\x1b[A')                          { this.cursor = Math.max(0, this.cursor - 1); this.render(); }
    else if (k === '\x1b[B')                          { this.cursor = Math.min(this.steps.length - 1, this.cursor + 1); this.render(); }
    else if (k === '\r' || k === '\n')                { this._busy = true; this.executeStep(this.cursor).finally(() => { this._busy = false; }); }
    else if (k === 'q' || k === 'Q' || k === '\x03') { this.exit(); }
  }

  _setupKeyboard() {
    this._handler = buf => this._onData(buf);
    process.stdin.on('data', this._handler);
  }

  _removeKeyboard() {
    if (this._handler) {
      process.stdin.removeListener('data', this._handler);
      this._handler = null;
    }
  }

  // Enter/空格/任意非 q 键 → 确认
  _waitConfirm() {
    return new Promise(resolve => {
      process.stdin.once('data', buf => {
        const k = buf.toString();
        resolve(k !== 'q' && k !== 'Q' && k !== '\x03');
      });
    });
  }

  // 严格确认：只接受 y/Y 键（用于 input 步骤的最终执行确认）
  // 规避 readline 关闭后残留 Enter 误触发的问题
  _waitYesConfirm() {
    return new Promise(resolve => {
      const handler = (buf) => {
        const k = buf.toString().toLowerCase();
        // 过滤掉残留的 Enter/换行（来自 readline）
        if (k === '\r' || k === '\n' || k === '\r\n') return;
        process.stdin.removeListener('data', handler);
        resolve(k === 'y');
      };
      process.stdin.on('data', handler);
    });
  }

  _waitAnyKey() {
    return new Promise(resolve => process.stdin.once('data', resolve));
  }

  // ── 入口 & 退出 ──────────────────────────────────────────────

  run() {
    process.stdin.setRawMode(true);
    process.stdin.resume();
    this._setupKeyboard();

    // 终端 resize 时自动重绘
    this._resizeHandler = () => this.render();
    process.stdout.on('resize', this._resizeHandler);

    this.render();
  }

  exit() {
    this._removeKeyboard();

    // 清理 resize 监听
    if (this._resizeHandler) {
      process.stdout.removeListener('resize', this._resizeHandler);
      this._resizeHandler = null;
    }

    if (this.onExit) {
      this.onExit();
    } else {
      process.stdin.setRawMode(false);
      process.stdin.pause();
      console.log('\n  ' + chalk.cyan('再见！👋') + '\n');
      process.exit(0);
    }
  }
}

module.exports = { WizardRunner };
