#!/usr/bin/env node
/**
 * TFDS B-End 全自动安装脚本
 *
 * 在业务项目根目录（或空文件夹）执行：
 *   npx -y -p @tfdesign/b-end@latest tfds-b-end-setup
 *
 * 指定目标项目：
 *   npx -y -p @tfdesign/b-end@latest tfds-b-end-setup -- --target /path/to/app
 *
 * 自动完成（按需判断，已满足则跳过）：
 *  1. 检测 package.json，不存在则自动创建最小 Vite + React 项目
 *  2. 检测 tailwindcss + @tailwindcss/vite，缺失则自动安装
 *  3. 检测 vite.config，缺少 tailwindcss() 插件则自动写入
 *  4. 检测入口 CSS，缺少 @import "tailwindcss" 则自动写入（找不到则创建 src/index.css）
 *  5. 安装 @tfdesign/b-end（触发 postinstall：自动复制 Skill + CSS 补丁）
 *  6. 运行接入自检，输出结果
 */

import { execSync } from 'node:child_process';
import fs from 'node:fs';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import { printInstallSuccessMessage } from './install-success-message.mjs';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const pkgRoot = path.resolve(__dirname, '..');
const cwd = process.cwd();
const publicNpmPackageName = '@tfdesign/b-end';
const skillPublicName = '产品设计-体服平台B端-界面生成';
const preferredSetupCommand = 'npx -y -p @tfdesign/b-end@latest tfds-b-end-setup';

function parseTargetArg() {
  const args = process.argv.slice(2);
  const targetIndex = args.findIndex((arg) => arg === '--target' || arg === '-t');
  if (targetIndex >= 0 && args[targetIndex + 1]) return args[targetIndex + 1];
  const inline = args.find((arg) => arg.startsWith('--target='));
  if (inline) return inline.slice('--target='.length);
  return process.env.TFDS_TARGET || '';
}

function hasPackageJson(dir) {
  return fs.existsSync(path.join(dir, 'package.json'));
}

function readJsonSafe(file) {
  try {
    return JSON.parse(fs.readFileSync(file, 'utf8'));
  } catch {
    return null;
  }
}

function getNodeMajorVersion() {
  return Number.parseInt(String(process.versions.node || '0').split('.')[0], 10) || 0;
}

function hasUsableNpm() {
  try {
    execSync('npm --version', { stdio: 'pipe', encoding: 'utf8' });
    return true;
  } catch {
    return false;
  }
}

function printEnvGuide() {
  console.log('\n╔══════════════════════════════════════════════════════╗');
  console.log('║        现在还不能开始安装 TFDS                      ║');
  console.log('╚══════════════════════════════════════════════════════╝');
  console.log('\n  开始前请先确认：');
  console.log('    1. 本机已安装 Node.js 18 或更高版本');
  console.log('    2. npm 可以在终端中直接使用');
  console.log('\n  你只需要这样处理：');
  console.log('    • 安装或升级到 Node.js LTS（推荐 20+）');
  console.log('    • 安装完成后，重新打开 IDE 或终端，再执行：');
  console.log(`      ${preferredSetupCommand}`);
  console.log('');
}

function ensureRuntimePrerequisites() {
  const nodeMajor = getNodeMajorVersion();
  if (nodeMajor < 18) {
    printEnvGuide();
    console.log(`  你当前安装的 Node.js 版本：${process.versions.node}`);
    console.log('  继续安装需要 Node.js 18 或更高版本。\n');
    process.exit(1);
  }

  if (!hasUsableNpm()) {
    printEnvGuide();
    console.log(`  你当前安装的 Node.js 版本：${process.versions.node}`);
    console.log('  目前没有找到可用的 npm。请确认 Node.js 安装完整，或重新安装 Node.js LTS。\n');
    process.exit(1);
  }
}

function isTfdsPackageDir(dir) {
  const pkg = readJsonSafe(path.join(dir, 'package.json'));
  return pkg?.name === '@tfdesign/b-end';
}

function findNearestBusinessProject(startDir) {
  let cur = path.resolve(startDir);
  while (true) {
    if (hasPackageJson(cur) && !isTfdsPackageDir(cur)) return cur;
    const next = path.dirname(cur);
    if (next === cur) return null;
    cur = next;
  }
}

function resolveProjectRoot() {
  const explicitTarget = parseTargetArg();
  if (explicitTarget) return path.resolve(cwd, explicitTarget);

  // 正常情况：IDE 打开的就是业务项目根。
  if (hasPackageJson(cwd) && !isTfdsPackageDir(cwd)) return cwd;

  return findNearestBusinessProject(cwd) || cwd;
}

const projectRoot = resolveProjectRoot();

ensureRuntimePrerequisites();

function log(msg)  { console.log(`\n[安装向导] ${msg}`); }
function ok(msg)   { console.log(`  ✓ ${msg}`); }
function warn(msg) { console.log(`  ⚠ ${msg}`); }

function ensureDir(dir) {
  fs.mkdirSync(dir, { recursive: true });
}

function writeIfMissing(file, content) {
  if (fs.existsSync(file)) return false;
  ensureDir(path.dirname(file));
  fs.writeFileSync(file, content, 'utf8');
  return true;
}

function toPackageName(name) {
  const safe = String(name || 'tfds-app')
    .toLowerCase()
    .replace(/[^a-z0-9._-]+/g, '-')
    .replace(/^-+|-+$/g, '');
  return safe || 'tfds-app';
}

function createMinimalViteReactProject(root) {
  const appName = toPackageName(path.basename(root));
  writeIfMissing(path.join(root, 'package.json'), `${JSON.stringify({
    name: appName,
    private: true,
    version: '0.0.0',
    type: 'module',
    scripts: {
      dev: 'vite',
      build: 'vite build',
      preview: 'vite preview',
    },
    dependencies: {},
    devDependencies: {},
  }, null, 2)}\n`);

  writeIfMissing(path.join(root, 'index.html'), `<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
`);

  writeIfMissing(path.join(root, 'vite.config.js'), `import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';

export default defineConfig({
  plugins: [react(), tailwindcss()],
});
`);

  writeIfMissing(path.join(root, 'src', 'main.jsx'), `import React from 'react';
import { createRoot } from 'react-dom/client';
import './index.css';
import App from './App.jsx';

createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
`);

  writeIfMissing(path.join(root, 'src', 'App.jsx'), `export default function App() {
  return (
    <main style={{ padding: 24, fontFamily: 'system-ui, sans-serif' }}>
      <h1>TFDS Ready</h1>
      <p>项目已初始化，可以开始接入 TFDS 设计规范。</p>
    </main>
  );
}
`);

  writeIfMissing(path.join(root, 'src', 'index.css'), '@import "tailwindcss";\n');
}

function readProjectPackageJson() {
  return JSON.parse(fs.readFileSync(path.join(projectRoot, 'package.json'), 'utf8'));
}

function getMergedDeps(pkg) {
  return {
    ...pkg.dependencies,
    ...pkg.devDependencies,
    ...pkg.peerDependencies,
  };
}

function resolveInstalledPackageDir(pkgName) {
  return path.join(projectRoot, 'node_modules', ...pkgName.split('/'));
}

function hasInstalledPackage(pkgName) {
  return fs.existsSync(resolveInstalledPackageDir(pkgName));
}

function getMissingPackages(pkg, pkgNames) {
  const allDeps = getMergedDeps(pkg);
  return pkgNames.filter((name) => !allDeps[name] || !hasInstalledPackage(name));
}

function formatPackageList(pkgNames) {
  return pkgNames.join(' / ');
}

function syncInstalledTfdsSkills() {
  const skillSyncScript = path.join(
    projectRoot,
    'node_modules',
    '@tfdesign',
    'b-end',
    'scripts',
    'postinstall-cursor-skill.mjs',
  );

  if (!fs.existsSync(skillSyncScript)) {
    warn('没有找到 skill 同步脚本，已跳过自动刷新。后续可重新执行一次 npm install @tfdesign/b-end。');
    return;
  }

  console.log('  正在刷新本地 skill 描述和规则文件...');
  if (!run(`node "${skillSyncScript}"`, {
    inherit: true,
    env: {
      SKIP_TFDS_CSS_PATCH: '1',
    },
  })) {
    warn('skill 自动刷新未完成。你可以稍后重新执行当前安装脚本，或手动运行：');
    warn('node node_modules/@tfdesign/b-end/scripts/postinstall-cursor-skill.mjs');
    return;
  }

  ok('skill 已同步到本地 IDE 目录');
}

function readJsonFileSafe(file) {
  try {
    if (!fs.existsSync(file)) return null;
    return JSON.parse(fs.readFileSync(file, 'utf8'));
  } catch {
    return null;
  }
}

function listFilesRecursive(dir, results = []) {
  if (!fs.existsSync(dir)) return results;
  for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
    if (entry.name.startsWith('.') || ['node_modules', 'dist', 'build', 'coverage'].includes(entry.name)) continue;
    const fullPath = path.join(dir, entry.name);
    if (entry.isDirectory()) {
      listFilesRecursive(fullPath, results);
      continue;
    }
    if (/\.(jsx?|tsx?)$/.test(entry.name)) {
      results.push(fullPath);
    }
  }
  return results;
}

function getComponentEntryMap(indexJson) {
  const entries = Array.isArray(indexJson?.components) ? indexJson.components : [];
  return new Map(entries.map((entry) => [entry.name, entry]));
}

function getUsedTfdsComponentNames() {
  const sourceRoots = ['src', 'app', 'pages', 'components']
    .map((rel) => path.join(projectRoot, rel))
    .filter((dir, index, all) => fs.existsSync(dir) && all.indexOf(dir) === index);
  const files = sourceRoots.flatMap((dir) => listFilesRecursive(dir));
  const used = new Set();

  for (const file of files) {
    const content = fs.readFileSync(file, 'utf8');
    const importPattern = /import\s*\{([^}]+)\}\s*from\s*['"]@tfdesign\/b-end['"]/g;
    let match;
    while ((match = importPattern.exec(content))) {
      for (const rawName of match[1].split(',')) {
        const importedName = rawName.trim().split(/\s+as\s+/i)[0]?.trim();
        if (importedName) used.add(importedName);
      }
    }
  }

  return [...used].sort((a, b) => a.localeCompare(b));
}

function getChangedUsedComponents(oldIndex, nextIndex, usedComponentNames) {
  const oldMap = getComponentEntryMap(oldIndex);
  const nextMap = getComponentEntryMap(nextIndex);
  return usedComponentNames
    .filter((name) => oldMap.has(name) && nextMap.has(name))
    .filter((name) => JSON.stringify(oldMap.get(name)) !== JSON.stringify(nextMap.get(name)))
    .map((name) => ({
      ...nextMap.get(name),
      changedSections: getChangedEntrySections(oldMap.get(name), nextMap.get(name)),
    }));
}

function getChangedEntrySections(oldEntry, nextEntry) {
  const sectionLabels = [
    ['description', '说明'],
    ['props', 'Props'],
    ['rules', '规则'],
    ['examples', '示例'],
    ['usage', '用法'],
    ['tokens', 'Token'],
    ['keywords', '关键词'],
    ['import', '导入方式'],
  ];

  const changed = sectionLabels
    .filter(([key]) => JSON.stringify(oldEntry?.[key]) !== JSON.stringify(nextEntry?.[key]))
    .map(([, label]) => label);

  return changed.length > 0 ? changed : ['组件索引'];
}

function printUpgradeImpactPreflight() {
  const installedPkgDir = resolveInstalledPackageDir(publicNpmPackageName);
  const installedPkg = readJsonFileSafe(path.join(installedPkgDir, 'package.json'));
  const nextPkg = readJsonFileSafe(path.join(pkgRoot, 'package.json'));
  const oldIndex = readJsonFileSafe(path.join(installedPkgDir, 'skills', 'tfds', 'components.index.json'));
  const nextIndex = readJsonFileSafe(path.join(pkgRoot, 'skills', 'tfds', 'components.index.json'));

  if (!installedPkg?.version || !nextPkg?.version || installedPkg.version === nextPkg.version || !oldIndex || !nextIndex) {
    return;
  }

  const usedComponentNames = getUsedTfdsComponentNames();
  const changedComponents = getChangedUsedComponents(oldIndex, nextIndex, usedComponentNames);

  console.log('\n╔══════════════════════════════════════════════════════╗');
  console.log('║        TFDS 组件升级影响预检                         ║');
  console.log('╚══════════════════════════════════════════════════════╝');
  console.log(`\n  检测到你正在更新 @tfdesign/b-end：v ${installedPkg.version} → v ${nextPkg.version}`);

  if (usedComponentNames.length === 0) {
    console.log('  暂未在项目源码中检测到从 @tfdesign/b-end import 的已有组件。');
  } else if (changedComponents.length === 0) {
    console.log(`  已检测到项目正在使用：${usedComponentNames.join('、')}`);
    console.log('  这些组件在新版组件索引中未发现规则/Props/示例变化。');
  } else {
    console.log('\n  以下是“项目已使用”且“新版组件索引有变化”的组件，请先人工判断是否要同步界面：');
    for (const component of changedComponents) {
      console.log(`    • ${component.name}`);
      console.log(`      更新范围：${component.changedSections.join('、')}`);
      console.log(`      说明：${component.description || '组件规范有更新'}`);
    }
  }

  console.log('\n  重要约束：');
  console.log('    • 本安装脚本只更新 npm 包、主题和 Skill，不会自动改你的页面组件代码。');
  console.log('    • 如果后续让 AI 按新版组件样式更新已有界面，AI 必须先列出影响清单并询问你。');
  console.log('    • 未经你明确确认，严禁把已有页面组件自动改成新版样式。\n');
}

/**
 * 执行命令，全程静默——进度由调用方用中文打印。
 * 失败时打印错误摘要（stderr 前 20 行）便于排查。
 * @param {string} cmd
 * @param {{ inherit?: boolean, env?: Record<string, string | undefined> }} [opts]  inherit=true 时保留原始输出（用于交互式命令）
 */
function run(cmd, opts = {}) {
  try {
    execSync(cmd, {
      stdio: opts.inherit ? 'inherit' : 'pipe',
      cwd: projectRoot,
      encoding: 'utf8',
      env: {
        ...process.env,
        ...opts.env,
      },
    });
    return true;
  } catch (e) {
    if (!opts.inherit) {
      const errLines = (e.stderr || e.stdout || '').split('\n').slice(0, 20).join('\n');
      if (errLines.trim()) {
        console.log('  --- 错误详情（英文）---');
        console.log(errLines);
        console.log('  ----------------------');
      }
    }
    return false;
  }
}

console.log('\n╔══════════════════════════════════════════════════════╗');
console.log('║         TFDS 页面生成能力安装向导                    ║');
console.log('╚══════════════════════════════════════════════════════╝');
console.log(`\n  环境检查已通过：Node.js ${process.versions.node}，npm 可用`);
console.log(`\n  安装包位置：${pkgRoot}`);
console.log(`  准备安装到：${projectRoot}`);

// ─────────────────────────────────────────────────────────────────
// Step 1：确认 package.json 存在
// ─────────────────────────────────────────────────────────────────
log('第 1 步 / 6  准备项目基础环境...');
const pkgPath = path.join(projectRoot, 'package.json');
const bootstrapDeps = ['react', 'react-dom', 'vite', '@vitejs/plugin-react'];
let createdProjectShell = false;

if (!fs.existsSync(pkgPath)) {
  console.log('\n  ⚠ 还没有检测到项目配置，正在为你准备一个可运行的 React 项目...');
  createMinimalViteReactProject(projectRoot);
  createdProjectShell = true;
}

let pkg = readProjectPackageJson();
if (pkg.name === '@tfdesign/b-end') {
  console.log('\n  ✗ 当前打开的是 TFDS 安装包目录，不是你的业务项目目录。');
  console.log('  请改用 --target 指定你的项目根目录，例如：');
  console.log(`    node "${path.join(pkgRoot, 'scripts', 'setup.mjs')}" --target "/path/to/your-app"\n`);
  process.exit(0);
}

const missingBootstrapDeps = getMissingPackages(pkg, bootstrapDeps);
if (missingBootstrapDeps.length > 0) {
  if (createdProjectShell) {
    console.log('  正在安装项目基础依赖，通常需要 10-60 秒...');
  } else {
    warn(`检测到上次安装还没有完成，正在继续补齐项目基础依赖：${formatPackageList(missingBootstrapDeps)}`);
    console.log('  无需重新开始，脚本会从当前进度继续...');
  }

  if (!run(`npm install ${bootstrapDeps.join(' ')}`, { inherit: true })) {
    warn('项目基础依赖暂未安装完成。这通常和网络或终端权限有关。');
    warn('建议切到系统终端重新执行同一脚本，网络恢复后会从当前进度继续。');
    process.exit(1);
  }

  pkg = readProjectPackageJson();
  ok('项目基础环境已准备好');
  if (createdProjectShell) {
    console.log('  ✓ 项目已准备完成，继续安装 TFDS 页面生成能力...');
  }
} else {
  ok('项目配置和基础依赖已准备好');
}

// ─────────────────────────────────────────────────────────────────
// Step 2：检测并安装 tailwindcss + @tailwindcss/vite
// ─────────────────────────────────────────────────────────────────
log('第 2 步 / 6  准备页面样式能力...');
const tailwindDeps = ['tailwindcss', '@tailwindcss/vite'];
const missingTailwindDeps = getMissingPackages(pkg, tailwindDeps);

if (missingTailwindDeps.length === 0) {
  ok('页面样式依赖已准备好，跳过');
} else {
  warn(`检测到页面样式能力还未准备完整，正在继续补齐：${formatPackageList(missingTailwindDeps)}`);
  console.log('  这一步通常也需要一点时间，请稍候...');
  if (!run(`npm install tailwindcss @tailwindcss/vite`, { inherit: true })) {
    warn('页面样式依赖暂未安装完成。建议切到系统终端重新执行同一脚本。');
    warn('如需手动处理，可执行：npm install tailwindcss @tailwindcss/vite');
  } else {
    pkg = readProjectPackageJson();
    ok('页面样式依赖已准备好');
  }
}

// ─────────────────────────────────────────────────────────────────
// Step 3：检测 vite.config，缺少 tailwindcss() 插件则自动写入
// ─────────────────────────────────────────────────────────────────
log('第 3 步 / 6  检查项目配置...');
const viteConfigCandidates = [
  'vite.config.js', 'vite.config.ts',
  'vite.config.mjs', 'vite.config.mts',
];
let viteConfigPath = null;
for (const f of viteConfigCandidates) {
  const p = path.join(projectRoot, f);
  if (fs.existsSync(p)) { viteConfigPath = p; break; }
}

if (!viteConfigPath) {
  warn('没有找到项目配置文件，已跳过自动更新。后续请手动在 vite.config 中加入 tailwindcss() 插件。');
} else {
  let viteContent = fs.readFileSync(viteConfigPath, 'utf8');
  const hasPlugin = /tailwindcss\s*\(\s*\)/.test(viteContent);

  if (hasPlugin) {
    ok(`${path.basename(viteConfigPath)} 已包含页面样式插件，跳过`);
  } else {
    // 加 import（如果没有）
    if (!/@tailwindcss\/vite/.test(viteContent)) {
      viteContent = `import tailwindcss from '@tailwindcss/vite';\n` + viteContent;
    }
    // 在 plugins 数组里插入 tailwindcss()
    if (/plugins\s*:\s*\[/.test(viteContent)) {
      viteContent = viteContent.replace(
        /plugins\s*:\s*\[/,
        'plugins: [tailwindcss(), '
      );
    } else {
      warn('项目配置文件里没有找到 plugins 数组，后续请手动加入 tailwindcss() 插件。');
    }
    fs.writeFileSync(viteConfigPath, viteContent, 'utf8');
    ok(`${path.basename(viteConfigPath)} 已自动更新`);
  }
}

// ─────────────────────────────────────────────────────────────────
// Step 4：检测入口 CSS，缺少 @import "tailwindcss" 则自动写入
// ─────────────────────────────────────────────────────────────────
log('第 4 步 / 6  检查页面样式入口...');
const cssCandidates = [
  'src/index.css', 'src/main.css', 'src/styles.css',
  'src/global.css', 'app/globals.css', 'styles/globals.css', 'index.css',
];

let cssHandled = false;
for (const rel of cssCandidates) {
  const cssPath = path.join(projectRoot, rel);
  if (!fs.existsSync(cssPath)) continue;

  const content = fs.readFileSync(cssPath, 'utf8');
  if (/@import\s+["']tailwindcss["']/.test(content)) {
    ok(`${rel} 已包含页面样式入口，跳过`);
    cssHandled = true;
    break;
  }
  // 在文件顶部插入
  fs.writeFileSync(cssPath, `@import "tailwindcss";\n` + content, 'utf8');
  ok(`${rel} 已自动补齐页面样式入口`);
  cssHandled = true;
  break;
}

if (!cssHandled) {
  const defaultCss = path.join(projectRoot, 'src', 'index.css');
  fs.mkdirSync(path.dirname(defaultCss), { recursive: true });
  fs.writeFileSync(defaultCss, '@import "tailwindcss";\n', 'utf8');
  ok('已创建 src/index.css 并补齐页面样式入口');
}

// ─────────────────────────────────────────────────────────────────
// Step 5：安装 @tfdesign/b-end（触发 postinstall）
// ─────────────────────────────────────────────────────────────────
log('第 5 步 / 6  安装 TFDS 页面生成能力...');
printUpgradeImpactPreflight();
const installCmd = `npm install react react-dom lucide-react ${publicNpmPackageName} --save`;
const tfdsDeps = ['lucide-react', '@tfdesign/b-end'];
const missingTfdsDeps = getMissingPackages(pkg, tfdsDeps);

if (missingTfdsDeps.length === 0) {
  ok('TFDS 页面生成能力已准备好，跳过');
} else {
  warn(`检测到 TFDS 页面生成能力还未准备完整，正在继续安装：${formatPackageList(missingTfdsDeps)}`);
  console.log('  正在安装 TFDS 页面生成能力，请稍候（通常需要 10-60 秒）...');
  if (!run(installCmd, { inherit: true })) {
    warn('TFDS 页面生成能力暂未安装完成。建议切到系统终端重新执行同一脚本。');
    warn('网络恢复后，脚本会从当前进度继续。');
    warn(`如需手动执行，可运行：${installCmd}`);
    process.exit(1);
  }
  pkg = readProjectPackageJson();
  ok('TFDS 页面生成能力已安装完成');
}

syncInstalledTfdsSkills();

// ─────────────────────────────────────────────────────────────────
// Step 6：运行接入自检
// ─────────────────────────────────────────────────────────────────
log('第 6 步 / 6  检查是否可以开始使用...\n');
const checkScript = path.join(
  projectRoot, 'node_modules', '@tfdesign', 'b-end',
  'scripts', 'check-tfds-integration.mjs'
);

if (fs.existsSync(checkScript)) {
  // 自检脚本输出均为中文，使用 inherit 直接展示
  run(`node "${checkScript}"`, { inherit: true });
} else {
  warn('没有找到自动检查脚本。你可以稍后手动执行：');
  warn('node node_modules/@tfdesign/b-end/scripts/check-tfds-integration.mjs');
}

printInstallSuccessMessage({ projectRoot, pkgRoot });
