#!/usr/bin/env node
"use strict";

/**
 * scripts/migrate-control-plane-to-sqlite.js
 *
 * Phase 0 dry-run 迁移工具 — 读取本地 control-plane 数据和工作区清单，
 * 生成 migration-report.json 建议（不写 DB，不改现有文件）。
 *
 * 用法：
 *   node scripts/migrate-control-plane-to-sqlite.js --dry-run --out /tmp/report.json
 *   node scripts/migrate-control-plane-to-sqlite.js --dry-run --access-policy-path /custom/path/access-policy.json
 *   node scripts/migrate-control-plane-to-sqlite.js --dry-run --users-root /custom/users/root
 *   node scripts/migrate-control-plane-to-sqlite.js --dry-run --skip-users
 *
 * 参考设计稿：
 *   design/02-convex-rust-long-term-architecture/process/2-8-convex-replace-with-rust-sqlite-control-plane-v1.md
 *
 * 如运行时文件系统扫描过慢，可使用 --from-snapshot 传入预收集的数据快照：
 *   1. 用 Reasonix 工具扫描：list_directory + read_file 收集所有用户 workpace.json
 *   2. 写入 JSON 文件（格式见 README）
 *   3. 传入工具： --from-snapshot /tmp/data-snapshot.json --skip-users
 */

const fs = require("node:fs");
const path = require("node:path");

// ─── 默认路径 ───────────────────────────────────────────────
const DEFAULT_DATA_ROOT = "/mnt/Data1T/Mnote_data";
const DEFAULT_ACCESS_POLICY = path.join(DEFAULT_DATA_ROOT, "control-plane", "access-policy.json");
const DEFAULT_USERS_ROOT = path.join(DEFAULT_DATA_ROOT, "users");

// ─── 版本标记 ───────────────────────────────────────────────
const TOOL_VERSION = "0.1.0";
const MIGRATION_PHASE = "Phase 0: dry-run & inventory";

// ─── CLI 参数解析 ────────────────────────────────────────────
function usage() {
  return [
    `migrate-control-plane-to-sqlite.js v${TOOL_VERSION}`,
    "",
    "用法：",
    "  node scripts/migrate-control-plane-to-sqlite.js --dry-run --out <路径>",
    "",
    "参数：",
    "  --dry-run                  必要参数。启动干跑模式（仅扫描、不写 DB）。",
    "  --out <路径>                输出 migration-report.json 路径。",
    "  --access-policy-path <路径> 自定义 access-policy.json 路径。",
    "  --users-root <路径>         自定义用户根目录。",
    "  --from-snapshot <路径>      从预收集的数据快照文件读取，替代直接文件系统扫描。",
    "  --skip-users               跳过用户扫描（仅读取 access-policy）。",
    "  --verbose                  输出详细扫描日志。",
    "  --help, -h                 显示此帮助。",
    "",
    "示例：",
    "  node scripts/migrate-control-plane-to-sqlite.js --dry-run --out /tmp/mnote-control-plane-migration-report.json",
    "  node scripts/migrate-control-plane-to-sqlite.js --dry-run --out ./migration-report.json --verbose",
    "  node scripts/migrate-control-plane-to-sqlite.js --dry-run --from-snapshot ./data-snapshot.json --out /tmp/report.json",
  ].join("\n");
}

function parseArgs(argv) {
  const opts = {
    dryRun: false,
    outPath: null,
    accessPolicyPath: DEFAULT_ACCESS_POLICY,
    usersRoot: DEFAULT_USERS_ROOT,
    fromSnapshot: null,
    skipUsers: false,
    verbose: false,
  };

  for (let i = 0; i < argv.length; i++) {
    const arg = argv[i];
    if (arg === "--help" || arg === "-h") {
      console.log(usage());
      process.exit(0);
    }
    if (arg === "--dry-run") {
      opts.dryRun = true;
      continue;
    }
    if (arg === "--out") {
      opts.outPath = argv[++i];
      if (!opts.outPath) throw new Error("--out 需要参数");
      continue;
    }
    if (arg === "--access-policy-path") {
      opts.accessPolicyPath = argv[++i];
      if (!opts.accessPolicyPath) throw new Error("--access-policy-path 需要参数");
      continue;
    }
    if (arg === "--users-root") {
      opts.usersRoot = argv[++i];
      if (!opts.usersRoot) throw new Error("--users-root 需要参数");
      continue;
    }
    if (arg === "--from-snapshot") {
      opts.fromSnapshot = argv[++i];
      if (!opts.fromSnapshot) throw new Error("--from-snapshot 需要参数");
      continue;
    }
    if (arg === "--skip-users") {
      opts.skipUsers = true;
      continue;
    }
    if (arg === "--verbose") {
      opts.verbose = true;
      continue;
    }
    throw new Error(`未知参数：${arg}\n\n${usage()}`);
  }

  if (!opts.dryRun) {
    throw new Error("必须指定 --dry-run（当前版本仅支持干跑模式）\n\n" + usage());
  }

  return opts;
}

// ─── 扫描工具函数 ──────────────────────────────────────────

function readJsonSafe(filePath, label) {
  try {
    if (!fs.existsSync(filePath)) {
      return { ok: false, error: `${label} 文件不存在：${filePath}` };
    }
    const raw = fs.readFileSync(filePath, "utf8");
    const data = JSON.parse(raw);
    return { ok: true, data };
  } catch (err) {
    return { ok: false, error: `读取 ${label} 失败：${err.message}` };
  }
}

function collectUsers(usersRoot) {
  const results = [];
  if (!fs.existsSync(usersRoot)) {
    return { ok: false, error: `用户目录不存在：${usersRoot}`, users: [] };
  }

  const entries = fs.readdirSync(usersRoot, { withFileTypes: true });

  for (const entry of entries) {
    if (!entry.isDirectory()) continue;
    if (entry.name.startsWith(".")) continue;

    const userId = entry.name;
    const userDir = path.join(usersRoot, userId);
    const workspacesDir = path.join(userDir, "workspaces");

    const userEntry = {
      userId,
      userDir,
      workspacesDir,
      workspaceCount: 0,
      workspaces: [],
      errors: [],
    };

    if (!fs.existsSync(workspacesDir)) {
      userEntry.errors.push("无 workspaces 目录");
      results.push(userEntry);
      continue;
    }

    const wsEntries = fs.readdirSync(workspacesDir, { withFileTypes: true });
    for (const ws of wsEntries) {
      if (!ws.isDirectory()) continue;
      if (ws.name.startsWith(".")) continue;

      const wsDir = path.join(workspacesDir, ws.name);
      const wsMetaDir = path.join(wsDir, ".mnote");
      const wsMetaFile = path.join(wsMetaDir, "workspace.json");

      const wsEntry = {
        workspaceName: ws.name,
        workspaceDir: wsDir,
        metaFile: wsMetaFile,
        meta: null,
        pageCount: 0,
        pages: [],
      };

      // 读取 workspace.json
      const metaResult = readJsonSafe(wsMetaFile, `${userId}/${ws.name}/workspace.json`);
      if (metaResult.ok) {
        wsEntry.meta = metaResult.data;
      } else {
        userEntry.errors.push(`${ws.name}：${metaResult.error}`);
      }

      // 统计页面（一级 .md 文件）
      if (fs.existsSync(wsDir)) {
        const pageFiles = fs.readdirSync(wsDir).filter((f) => f.endsWith(".md"));
        wsEntry.pageCount = pageFiles.length;
        wsEntry.pages = pageFiles;
      }

      userEntry.workspaces.push(wsEntry);
    }

    userEntry.workspaceCount = userEntry.workspaces.length;
    results.push(userEntry);
  }

  return { ok: true, users: results };
}

// ─── 构建迁移报告 ──────────────────────────────────────────

function buildReport(opts, accessPolicyResult, usersResult) {
  const report = {
    meta: {
      toolVersion: TOOL_VERSION,
      migrationPhase: MIGRATION_PHASE,
      generatedAt: new Date().toISOString(),
      source: {
        accessPolicy: opts.accessPolicyPath,
        usersRoot: opts.usersRoot,
      },
      flags: {
        dryRun: true,
        skipUsers: opts.skipUsers,
      },
    },
    accessPolicy: {
      path: opts.accessPolicyPath,
      readOk: accessPolicyResult.ok,
      error: accessPolicyResult.ok ? null : accessPolicyResult.error,
      admins: accessPolicyResult.ok ? (accessPolicyResult.data.admins || []) : [],
      grants: accessPolicyResult.ok ? (accessPolicyResult.data.grants || []) : [],
    },
    users: {
      scanOk: usersResult.ok,
      error: usersResult.ok ? null : usersResult.error,
      total: usersResult.ok ? usersResult.users.length : 0,
      items: [],
    },
    suggestions: {
      users: [],
      workspaces: [],
      grants: [],
      legacyIdMap: [],
    },
    summary: {
      adminCount: 0,
      userCount: 0,
      workspaceCount: 0,
      grantCount: 0,
      legacyMappingCount: 0,
      errors: [],
    },
  };

  if (!usersResult.ok) {
    report.summary.errors.push(usersResult.error);
    return report;
  }

  for (const u of usersResult.users) {
    const userItem = {
      userId: u.userId,
      workspaceCount: u.workspaceCount,
      errors: u.errors,
      workspaces: [],
    };

    for (const ws of u.workspaces) {
      const wsItem = {
        workspaceName: ws.workspaceName,
        meta: ws.meta,
        pageCount: ws.pageCount,
      };
      userItem.workspaces.push(wsItem);

      // ── 生成 workspace 建议 ──
      if (ws.meta && ws.meta.workspaceId) {
        const parts = ws.meta.workspaceId.split(":");
        const newWsId = `sqlite-ws:${u.userId}:${ws.workspaceName}`;
        report.suggestions.workspaces.push({
          legacyWorkspaceId: ws.meta.workspaceId,
          suggestedNewId: newWsId,
          ownerId: u.userId,
          name: ws.workspaceName,
          rootPath: ws.workspaceDir,
          source: "workspace.json",
        });

        // ── 生成 user 建议 ──
        const existingUser = report.suggestions.users.find((x) => x.legacyUserId === u.userId);
        if (!existingUser) {
          // 检查是否在 access-policy 的 admins 中
          const isAdmin = accessPolicyResult.ok && (accessPolicyResult.data.admins || []).includes(u.userId);
          const newUserId = `usr_${u.userId.replace(/[^a-zA-Z0-9_-]/g, "_")}`;
          report.suggestions.users.push({
            legacyUserId: u.userId,
            suggestedNewId: newUserId,
            role: isAdmin ? "admin" : "user",
            isAdmin,
            defaultWorkspace: ws.workspaceName,
            source: "workspace.json",
          });

          // ── 生成 legacy_id_map 建议 ──
          report.suggestions.legacyIdMap.push({
            legacySystem: "local_workspace_manifest",
            legacyKind: "user",
            legacyId: u.userId,
            newKind: "user",
            newId: newUserId,
          });
        }
      }
    }

    report.users.items.push(userItem);
  }

  // ── 生成 grant 建议 ──
  if (accessPolicyResult.ok && accessPolicyResult.data.admins) {
    for (const adminId of accessPolicyResult.data.admins) {
      // 找到该 admin 对应的 user 建议
      const adminUser = report.suggestions.users.find((x) => x.legacyUserId === adminId);
      report.suggestions.grants.push({
        legacyAdminId: adminId,
        suggestedNewUserId: adminUser ? adminUser.suggestedNewId : adminId,
        type: "admin",
        rootUri: `mnote://users/${adminId}`,
        rootPath: path.join(opts.usersRoot, adminId),
        permission: "admin",
        capabilities: ["admin", "share", "ai"],
        source: "access-policy.json:admins",
      });
    }
  }

  if (accessPolicyResult.ok && accessPolicyResult.data.grants) {
    for (const grant of accessPolicyResult.data.grants) {
      report.suggestions.grants.push({
        legacySource: "access-policy.json:grants",
        source: grant,
        type: "directory_grant",
        ...grant,
      });
    }
  }

  // ── 生成 legacy_id_map 建议（workspaces）──
  for (const ws of report.suggestions.workspaces) {
    report.suggestions.legacyIdMap.push({
      legacySystem: "local_workspace_manifest",
      legacyKind: "workspace",
      legacyId: ws.legacyWorkspaceId,
      newKind: "workspace",
      newId: ws.suggestedNewId,
    });
  }

  // ── 填写概要 ──
  report.summary.adminCount = report.accessPolicy.admins.length;
  report.summary.userCount = report.users.total;
  report.summary.workspaceCount = report.suggestions.workspaces.length;
  report.summary.grantCount = report.suggestions.grants.length;
  report.summary.legacyMappingCount = report.suggestions.legacyIdMap.length;

  // 收集所有错误
  for (const u of usersResult.users) {
    for (const err of u.errors) {
      report.summary.errors.push(`[${u.userId}] ${err}`);
    }
  }

  return report;
}

// ─── 入口 ──────────────────────────────────────────────────

function main() {
  const opts = parseArgs(process.argv.slice(2));

  if (opts.verbose) {
    console.error(`[migrate-control-plane] 版本 ${TOOL_VERSION}`);
    console.error(`[migrate-control-plane] access-policy: ${opts.accessPolicyPath}`);
    console.error(`[migrate-control-plane] users-root: ${opts.usersRoot}`);
    console.error(`[migrate-control-plane] skip-users: ${opts.skipUsers}`);
    console.error(`[migrate-control-plane] dry-run: ${opts.dryRun}`);
  }

  // 1. 读取 access-policy.json（可能被快照覆盖，用 let）
  let accessPolicyResult = readJsonSafe(opts.accessPolicyPath, "access-policy.json");
  if (opts.verbose) {
    if (accessPolicyResult.ok) {
      console.error(`[migrate-control-plane] access-policy.json 读取成功`);
      console.error(`[migrate-control-plane]   admins: ${JSON.stringify(accessPolicyResult.data.admins)}`);
      console.error(`[migrate-control-plane]   grants: ${accessPolicyResult.data.grants ? accessPolicyResult.data.grants.length : 0} 条`);
    } else {
      console.error(`[migrate-control-plane] access-policy.json 读取失败: ${accessPolicyResult.error}`);
    }
  }

  // 2. 扫描用户（支持从快照文件读取 vs 直接文件系统扫描）
  let usersResult = { ok: true, users: [] };
  if (opts.fromSnapshot) {
    const snapshotResult = readJsonSafe(opts.fromSnapshot, "数据快照");
    if (snapshotResult.ok) {
      const snapshot = snapshotResult.data;
      if (snapshot.users) {
        usersResult = { ok: true, users: snapshot.users };
        if (opts.verbose) {
          console.error(`[migrate-control-plane] 从快照读取 ${snapshot.users.length} 个用户`);
        }
        // 如果快照中已有 accessPolicy，覆盖 access-policy.json 的读取
        if (snapshot.accessPolicy && !opts.accessPolicyPath) {
          const policyData = { ok: true, data: snapshot.accessPolicy };
          // 替换 accessPolicyResult
          accessPolicyResult = policyData;
          if (opts.verbose) {
            console.error(`[migrate-control-plane] 快照中的 accessPolicy 已生效`);
          }
        }
      } else {
        usersResult = { ok: false, error: "快照文件中缺少 users 字段", users: [] };
      }
    } else {
      usersResult = { ok: false, error: snapshotResult.error, users: [] };
    }
  } else if (!opts.skipUsers) {
    usersResult = collectUsers(opts.usersRoot);
    if (opts.verbose) {
      if (usersResult.ok) {
        console.error(`[migrate-control-plane] 用户扫描完成: ${usersResult.users.length} 个用户`);
        for (const u of usersResult.users) {
          console.error(`[migrate-control-plane]   ${u.userId}: ${u.workspaceCount} 个工作区`);
        }
      } else {
        console.error(`[migrate-control-plane] 用户扫描失败: ${usersResult.error}`);
      }
    }
  } else {
    if (opts.verbose) {
      console.error(`[migrate-control-plane] 用户扫描已跳过 (--skip-users)`);
    }
  }

  // 3. 构建报告
  const report = buildReport(opts, accessPolicyResult, usersResult);

  // 4. 输出
  const json = JSON.stringify(report, null, 2);

  if (opts.outPath) {
    fs.mkdirSync(path.dirname(opts.outPath), { recursive: true });
    fs.writeFileSync(opts.outPath, json, "utf8");
    if (opts.verbose) {
      console.error(`[migrate-control-plane] 报告已写入: ${opts.outPath}`);
    }
  }

  // stdout 只输出概要（便于管道处理）
  const brief = {
    ok: true,
    tool: "migrate-control-plane-to-sqlite",
    dryRun: true,
    summary: report.summary,
    outPath: opts.outPath || "(stdout)",
  };
  console.log(JSON.stringify(brief, null, 2));

  // 如果有错误但不太严重，仍然 exit 0
  // 严重错误（access-policy 读不到、用户目录不存在）通过 summary.errors 和 accessPolicy.error 体现
  const fatalCount = report.summary.errors.length;
  if (fatalCount > 0) {
    console.error(`[migrate-control-plane] 警告：扫描中发现 ${fatalCount} 个非致命错误，详见报告 errors`);
  }
}

if (require.main === module) {
  try {
    main();
  } catch (error) {
    console.error(error instanceof Error ? error.message : String(error));
    process.exit(1);
  }
}
