import {
  detectLaneOverlaps,
  loadLaneDefinitions
} from "./chunk-XKY65P2T.js";
import {
  createWUParser,
  detectMissingTrackedWorktrees,
  detectOrphanWorktrees,
  runCLI
} from "./chunk-2GXVIN57.js";
import {
  createWuPaths
} from "./chunk-6HO4GWJE.js";
import {
  CONFIG_FILES
} from "./chunk-DWMLTXKQ.js";
import {
  WORKSPACE_CONFIG_FILE_NAME,
  getConfig,
  getConfigFilePresence,
  getResolvedPaths
} from "./chunk-V6OJGLBA.js";

// src/doctor.ts
import * as fs from "fs";
import * as path from "path";
import { execFileSync } from "child_process";
var MANAGED_FILE_PATTERNS = [
  WORKSPACE_CONFIG_FILE_NAME,
  CONFIG_FILES.LANE_INFERENCE,
  "AGENTS.md",
  "CLAUDE.md"
];
var DOCTOR_OPTIONS = {
  verbose: {
    name: "verbose",
    flags: "-v, --verbose",
    description: "Show detailed output including all checks"
  },
  json: {
    name: "json",
    flags: "--json",
    description: "Output results as JSON"
  },
  deep: {
    name: "deep",
    flags: "--deep",
    description: "Run heavier checks including WU validation (slower)"
  }
};
function parseDoctorOptions() {
  const opts = createWUParser({
    name: "lumenflow-doctor",
    description: "Check LumenFlow safety components and configuration",
    options: Object.values(DOCTOR_OPTIONS)
  });
  return {
    verbose: opts.verbose ?? false,
    json: opts.json ?? false,
    deep: opts.deep ?? false
  };
}
function checkHusky(projectDir) {
  const huskyDir = path.join(projectDir, ".husky");
  const preCommit = path.join(huskyDir, "pre-commit");
  if (!fs.existsSync(huskyDir)) {
    return {
      passed: false,
      message: "Husky hooks not installed",
      details: "Run: pnpm install && pnpm prepare"
    };
  }
  if (!fs.existsSync(preCommit)) {
    return {
      passed: false,
      message: "Husky pre-commit hook missing",
      details: "Run: pnpm prepare"
    };
  }
  return {
    passed: true,
    message: "Husky hooks installed (.husky/pre-commit)"
  };
}
function checkSafeGit(projectDir) {
  let safeGitPath;
  try {
    const resolved = getResolvedPaths({ projectRoot: projectDir, strictWorkspace: true });
    safeGitPath = resolved.safeGitPath;
  } catch {
    safeGitPath = path.join(projectDir, "scripts", "safe-git");
  }
  const relativePath = path.relative(projectDir, safeGitPath);
  if (!fs.existsSync(safeGitPath)) {
    return {
      passed: false,
      message: "Safe-git wrapper missing",
      details: `The ${relativePath} file should exist to block destructive git commands`
    };
  }
  return {
    passed: true,
    message: `Safe-git wrapper present (${relativePath})`
  };
}
function checkAgentsMd(projectDir) {
  const agentsMdPath = path.join(projectDir, "AGENTS.md");
  if (!fs.existsSync(agentsMdPath)) {
    return {
      passed: false,
      message: "AGENTS.md missing",
      details: "Run: lumenflow init to create universal agent instructions"
    };
  }
  return {
    passed: true,
    message: "AGENTS.md exists"
  };
}
function checkLumenflowConfig(projectDir) {
  const { workspaceConfigExists } = getConfigFilePresence(projectDir);
  if (!workspaceConfigExists) {
    return {
      passed: false,
      message: `${WORKSPACE_CONFIG_FILE_NAME} missing`,
      details: `Run: pnpm workspace-init --yes`
    };
  }
  return {
    passed: true,
    message: `Workspace config present (${WORKSPACE_CONFIG_FILE_NAME})`
  };
}
function checkVendorConfigs(projectDir) {
  const claudeMdPath = path.join(projectDir, "CLAUDE.md");
  const cursorRulesPath = path.join(projectDir, ".cursor", "rules", "lumenflow.md");
  const windsurfRulesPath = path.join(projectDir, ".windsurf", "rules", "lumenflow.md");
  const clineRulesPath = path.join(projectDir, ".clinerules");
  const agentsMdPath = path.join(projectDir, "AGENTS.md");
  return {
    claude: {
      present: fs.existsSync(claudeMdPath),
      path: "CLAUDE.md"
    },
    cursor: {
      present: fs.existsSync(cursorRulesPath),
      path: ".cursor/rules/lumenflow.md"
    },
    windsurf: {
      present: fs.existsSync(windsurfRulesPath),
      path: ".windsurf/rules/lumenflow.md"
    },
    cline: {
      present: fs.existsSync(clineRulesPath),
      path: ".clinerules"
    },
    codex: {
      // Codex reads AGENTS.md directly
      present: fs.existsSync(agentsMdPath),
      path: "AGENTS.md"
    }
  };
}
function getCommandVersion(command, args) {
  try {
    const output = execFileSync(command, args, {
      encoding: "utf-8",
      stdio: ["pipe", "pipe", "pipe"]
    }).trim();
    return output;
  } catch {
    return "not found";
  }
}
function parseVersion(versionStr) {
  const match = versionStr.match(/(\d+)\.(\d+)\.?(\d+)?/);
  if (!match) {
    return [0, 0, 0];
  }
  return [parseInt(match[1], 10), parseInt(match[2], 10), parseInt(match[3] || "0", 10)];
}
function compareVersions(actual, required) {
  const actualParts = parseVersion(actual);
  const requiredParts = parseVersion(required);
  for (let i = 0; i < 3; i++) {
    if (actualParts[i] > requiredParts[i]) {
      return true;
    }
    if (actualParts[i] < requiredParts[i]) {
      return false;
    }
  }
  return true;
}
function checkLaneHealth(projectDir) {
  const lanes = loadLaneDefinitions(projectDir);
  if (lanes.length === 0) {
    return {
      passed: true,
      message: "No lane definitions found - skipping lane health check"
    };
  }
  const overlapResult = detectLaneOverlaps(lanes);
  if (overlapResult.hasOverlaps) {
    const overlapCount = overlapResult.overlaps.length;
    const firstOverlap = overlapResult.overlaps[0];
    const laneNames = firstOverlap.lanes.join(" <-> ");
    return {
      passed: false,
      message: `Lane overlap detected: ${overlapCount} overlap(s) found`,
      details: `First overlap: ${laneNames}. Run 'pnpm lane:health' for full report.`
    };
  }
  return {
    passed: true,
    message: `Lane configuration healthy (${lanes.length} lanes, no overlaps)`
  };
}
function checkPrerequisites() {
  const nodeVersion = getCommandVersion("node", ["--version"]);
  const pnpmVersion = getCommandVersion("pnpm", ["--version"]);
  const gitVersion = getCommandVersion("git", ["--version"]);
  const requiredNode = "22.0.0";
  const requiredPnpm = "9.0.0";
  const requiredGit = "2.0.0";
  const nodeOk = nodeVersion !== "not found" && compareVersions(nodeVersion, requiredNode);
  const pnpmOk = pnpmVersion !== "not found" && compareVersions(pnpmVersion, requiredPnpm);
  const gitOk = gitVersion !== "not found" && compareVersions(gitVersion, requiredGit);
  return {
    node: {
      passed: nodeOk,
      version: nodeVersion,
      required: `>=${requiredNode}`,
      message: nodeOk ? void 0 : `Node.js ${requiredNode}+ required`
    },
    pnpm: {
      passed: pnpmOk,
      version: pnpmVersion,
      required: `>=${requiredPnpm}`,
      message: pnpmOk ? void 0 : `pnpm ${requiredPnpm}+ required`
    },
    git: {
      passed: gitOk,
      version: gitVersion,
      required: `>=${requiredGit}`,
      message: gitOk ? void 0 : `Git ${requiredGit}+ required`
    }
  };
}
function getGitRepoRoot(projectDir) {
  try {
    const repoRoot = execFileSync("git", ["rev-parse", "--show-toplevel"], {
      cwd: projectDir,
      encoding: "utf-8",
      stdio: ["pipe", "pipe", "pipe"]
    }).trim();
    return repoRoot;
  } catch {
    return null;
  }
}
async function checkManagedFilesDirty(projectDir) {
  try {
    const repoRoot = getGitRepoRoot(projectDir);
    if (!repoRoot) {
      return {
        passed: true,
        files: [],
        message: "Git status check skipped (not a git repository)"
      };
    }
    const statusOutput = execFileSync("git", ["status", "--porcelain"], {
      cwd: repoRoot,
      encoding: "utf-8",
      stdio: ["pipe", "pipe", "pipe"]
    });
    const config = getConfig({ projectRoot: repoRoot });
    const managedDirPrefix = `${config.directories.wuDir.replace(/\\/g, "/").replace(/\/wu\/?$/, "/")}`;
    const dirtyFiles = [];
    const lines = statusOutput.split("\n").filter((line) => line.trim());
    for (const line of lines) {
      const filePath = line.slice(3).trim();
      if (!filePath) continue;
      const isManaged = MANAGED_FILE_PATTERNS.some((pattern) => filePath === pattern) || filePath.startsWith(managedDirPrefix);
      if (isManaged) {
        dirtyFiles.push(filePath);
      }
    }
    if (dirtyFiles.length > 0) {
      return {
        passed: false,
        files: dirtyFiles,
        message: `${dirtyFiles.length} managed file(s) have uncommitted changes`
      };
    }
    return {
      passed: true,
      files: [],
      message: "No uncommitted changes to managed files"
    };
  } catch {
    return {
      passed: true,
      files: [],
      message: "Git status check skipped (not a git repository)"
    };
  }
}
async function checkWorktreeSanity(projectDir) {
  try {
    execFileSync("git", ["rev-parse", "--git-dir"], {
      cwd: projectDir,
      encoding: "utf-8",
      stdio: ["pipe", "pipe", "pipe"]
    });
    const orphanResult = await detectOrphanWorktrees(projectDir);
    const missingTracked = await detectMissingTrackedWorktrees(projectDir);
    const orphanCount = orphanResult.orphans.length;
    const missingCount = missingTracked.length;
    const errorCount = orphanResult.errors.length;
    const totalIssues = orphanCount + missingCount + errorCount;
    const passed = totalIssues === 0;
    let message = "All worktrees are valid";
    if (!passed) {
      const parts = [];
      if (orphanCount > 0) parts.push(`${orphanCount} orphan(s)`);
      if (missingCount > 0) parts.push(`${missingCount} missing`);
      if (errorCount > 0) parts.push(`${errorCount} error(s)`);
      message = `Worktree issues: ${parts.join(", ")}`;
    }
    return {
      passed,
      orphans: orphanCount,
      stale: missingCount,
      // Combined non-orphan issues for API compat
      message
    };
  } catch {
    return {
      passed: true,
      orphans: 0,
      stale: 0,
      message: "Worktree check skipped (not a git repository)"
    };
  }
}
async function checkWUValidity(projectDir) {
  const wuDir = path.join(projectDir, createWuPaths({ projectRoot: projectDir }).WU_DIR());
  if (!fs.existsSync(wuDir)) {
    return {
      passed: true,
      total: 0,
      valid: 0,
      invalid: 0,
      warnings: 0,
      message: "No WU directory found"
    };
  }
  try {
    let validateOutput;
    let cliError = false;
    let cliErrorMessage = "";
    try {
      validateOutput = execFileSync("pnpm", ["wu:validate", "--all", "--no-strict"], {
        cwd: projectDir,
        encoding: "utf-8",
        stdio: ["pipe", "pipe", "pipe"],
        timeout: 6e4
        // 60 second timeout for full validation
      });
    } catch (e) {
      const execError = e;
      validateOutput = (execError.stdout || "") + (execError.stderr || "");
      const errorMsg = execError.message || "";
      if (errorMsg.includes("ERR_PNPM") || errorMsg.includes("ENOENT") || errorMsg.includes("Missing script") || errorMsg.includes("command not found") || execError.code === "ENOENT" || // If no recognizable wu:validate output, assume CLI failed
      !validateOutput.includes("[wu:validate]") && !validateOutput.includes("Valid:")) {
        cliError = true;
        cliErrorMessage = errorMsg.includes("Missing script") ? "wu:validate script not found" : errorMsg.includes("ENOENT") ? "pnpm command not available" : "wu:validate could not run";
      }
    }
    if (cliError) {
      return {
        passed: false,
        total: 0,
        valid: 0,
        invalid: 0,
        warnings: 0,
        message: `WU validation failed: ${cliErrorMessage}`
      };
    }
    const validMatch = validateOutput.match(/Valid:\s*(\d+)/);
    const invalidMatch = validateOutput.match(/Invalid:\s*(\d+)/);
    const warningMatch = validateOutput.match(/Warnings:\s*(\d+)/);
    const validCount = validMatch ? parseInt(validMatch[1], 10) : 0;
    const invalidCount = invalidMatch ? parseInt(invalidMatch[1], 10) : 0;
    const warningCount = warningMatch ? parseInt(warningMatch[1], 10) : 0;
    const total = validCount + invalidCount;
    const passed = invalidCount === 0;
    return {
      passed,
      total,
      valid: validCount,
      invalid: invalidCount,
      warnings: warningCount,
      message: passed ? total > 0 ? `All ${total} WU(s) valid${warningCount > 0 ? ` (${warningCount} warning(s))` : ""}` : "No WUs to validate" : `${invalidCount}/${total} WU(s) have issues`
    };
  } catch (e) {
    const error = e;
    return {
      passed: false,
      total: 0,
      valid: 0,
      invalid: 0,
      warnings: 0,
      message: `WU validation failed: ${error.message || "unknown error"}`
    };
  }
}
async function runDoctor(projectDir, options = {}) {
  const checks = {
    husky: checkHusky(projectDir),
    safeGit: checkSafeGit(projectDir),
    agentsMd: checkAgentsMd(projectDir),
    lumenflowConfig: checkLumenflowConfig(projectDir),
    // WU-1191: Lane health check
    laneHealth: checkLaneHealth(projectDir)
  };
  const vendorConfigs = checkVendorConfigs(projectDir);
  const prerequisites = checkPrerequisites();
  const managedFilesDirty = await checkManagedFilesDirty(projectDir);
  const worktreeSanity = await checkWorktreeSanity(projectDir);
  const workflowHealth = {
    managedFilesDirty,
    worktreeSanity
  };
  if (options.deep) {
    workflowHealth.wuValidity = await checkWUValidity(projectDir);
  }
  const criticalChecks = [checks.husky, checks.safeGit, checks.agentsMd, checks.lumenflowConfig];
  const allCriticalPassed = criticalChecks.every((check) => check.passed);
  let exitCode = 0;
  if (!allCriticalPassed) {
    exitCode = 2;
  } else if (!managedFilesDirty.passed || !worktreeSanity.passed || workflowHealth.wuValidity && !workflowHealth.wuValidity.passed) {
    exitCode = 1;
  }
  return {
    status: allCriticalPassed ? "ACTIVE" : "INCOMPLETE",
    exitCode,
    checks,
    vendorConfigs,
    prerequisites,
    workflowHealth
  };
}
async function runDoctorForInit(projectDir) {
  const result = await runDoctor(projectDir, { deep: false });
  let warnings = 0;
  let errors = 0;
  const criticalCheckKeys = ["husky", "safeGit", "agentsMd", "lumenflowConfig"];
  for (const [key, check] of Object.entries(result.checks)) {
    if (!check.passed) {
      if (criticalCheckKeys.includes(key)) {
        errors++;
      } else {
        warnings++;
      }
    }
  }
  for (const [, prereq] of Object.entries(result.prerequisites)) {
    if (!prereq.passed) {
      warnings++;
    }
  }
  if (result.workflowHealth) {
    if (!result.workflowHealth.managedFilesDirty.passed) warnings++;
    if (!result.workflowHealth.worktreeSanity.passed) warnings++;
  }
  const lines = [];
  lines.push("[lumenflow doctor] Quick health check...");
  if (result.exitCode === 0 && warnings === 0) {
    lines.push("  All checks passed");
  } else {
    if (!result.checks.husky.passed) {
      lines.push("  Error: Husky hooks not installed");
    }
    if (!result.checks.safeGit.passed) {
      lines.push("  Error: safe-git script not found");
    }
    if (!result.checks.agentsMd.passed) {
      lines.push("  Error: AGENTS.md not found");
    }
    if (!result.checks.lumenflowConfig.passed) {
      lines.push(`  Error: ${result.checks.lumenflowConfig.message}`);
      if (result.checks.lumenflowConfig.details) {
        lines.push(`    ${result.checks.lumenflowConfig.details}`);
      }
    }
    if (!result.checks.laneHealth.passed) {
      lines.push(`  Warning: Lane overlap detected - ${result.checks.laneHealth.message}`);
    }
    if (!result.prerequisites.node.passed) {
      lines.push(
        `  Warning: Node.js version ${result.prerequisites.node.version} (required: ${result.prerequisites.node.required})`
      );
    }
    if (!result.prerequisites.pnpm.passed) {
      lines.push(
        `  Warning: pnpm version ${result.prerequisites.pnpm.version} (required: ${result.prerequisites.pnpm.required})`
      );
    }
    if (!result.prerequisites.git.passed) {
      lines.push(
        `  Warning: Git version ${result.prerequisites.git.version} (required: ${result.prerequisites.git.required})`
      );
    }
    if (result.workflowHealth?.managedFilesDirty.files.length) {
      lines.push(
        `  Warning: ${result.workflowHealth.managedFilesDirty.files.length} managed file(s) have uncommitted changes`
      );
      for (const file of result.workflowHealth.managedFilesDirty.files.slice(0, 3)) {
        lines.push(`    -> ${file}`);
      }
    }
    if (result.workflowHealth?.worktreeSanity.orphans) {
      lines.push(
        `  Warning: ${result.workflowHealth.worktreeSanity.orphans} orphan worktree(s) found`
      );
    }
    if (result.workflowHealth && !result.workflowHealth.worktreeSanity.passed) {
      const wsSanity = result.workflowHealth.worktreeSanity;
      if (wsSanity.stale > 0 && wsSanity.orphans === 0) {
        lines.push(`  Warning: ${wsSanity.message}`);
      }
    }
  }
  return {
    blocked: false,
    // Never blocks init
    warnings,
    errors,
    output: lines.join("\n")
  };
}
function formatDoctorOutput(result) {
  const lines = [];
  lines.push("");
  lines.push("LumenFlow Health Check");
  lines.push("\u2500".repeat(45));
  lines.push("");
  lines.push("Core Safety Components:");
  const formatCheck = (check) => {
    const symbol = check.passed ? "\u2713" : "\u2717";
    return `  ${symbol} ${check.message}`;
  };
  lines.push(formatCheck(result.checks.husky));
  lines.push(formatCheck(result.checks.safeGit));
  lines.push(formatCheck(result.checks.agentsMd));
  lines.push(formatCheck(result.checks.lumenflowConfig));
  lines.push("");
  lines.push("Lane Health:");
  lines.push(formatCheck(result.checks.laneHealth));
  if (result.workflowHealth) {
    lines.push("");
    lines.push("Workflow Health:");
    const mfd = result.workflowHealth.managedFilesDirty;
    const mfdSymbol = mfd.passed ? "\u2713" : "\u26A0";
    lines.push(`  ${mfdSymbol} ${mfd.message}`);
    if (!mfd.passed && mfd.files.length > 0) {
      for (const file of mfd.files.slice(0, 5)) {
        lines.push(`     \u2192 ${file}`);
      }
      if (mfd.files.length > 5) {
        lines.push(`     \u2192 ... and ${mfd.files.length - 5} more`);
      }
    }
    const ws = result.workflowHealth.worktreeSanity;
    const wsSymbol = ws.passed ? "\u2713" : "\u26A0";
    lines.push(`  ${wsSymbol} ${ws.message}`);
    if (result.workflowHealth.wuValidity) {
      const wv = result.workflowHealth.wuValidity;
      const wvSymbol = wv.passed ? "\u2713" : "\u26A0";
      lines.push(`  ${wvSymbol} ${wv.message}`);
    }
  }
  lines.push("");
  lines.push("Vendor Configs:");
  const vendors = ["claude", "cursor", "windsurf", "cline", "codex"];
  for (const vendor of vendors) {
    const config = result.vendorConfigs[vendor];
    const symbol = config.present ? "\u2713" : "\u25CB";
    const status = config.present ? "present" : "not configured";
    lines.push(`  ${symbol} ${vendor}: ${status} (${config.path})`);
  }
  lines.push("");
  lines.push("Prerequisites:");
  const prereqs = ["node", "pnpm", "git"];
  for (const prereq of prereqs) {
    const check = result.prerequisites[prereq];
    const symbol = check.passed ? "\u2713" : "\u2717";
    const versionDisplay = check.version === "not found" ? "not found" : check.version;
    lines.push(`  ${symbol} ${prereq}: ${versionDisplay} (required: ${check.required})`);
  }
  lines.push("");
  lines.push("\u2500".repeat(45));
  lines.push(`LumenFlow safety: ${result.status}`);
  if (result.exitCode === 1) {
    lines.push("");
    lines.push("Warnings detected (exit code 1). Use --deep for full WU validation.");
  } else if (result.exitCode === 2) {
    lines.push("");
    lines.push("To fix missing components:");
    lines.push("  pnpm install && pnpm prepare  # Install Husky hooks");
    lines.push("  pnpm workspace-init --yes      # Create canonical workspace.yaml");
  }
  lines.push("");
  return lines.join("\n");
}
function formatDoctorJson(result) {
  return JSON.stringify(result, null, 2);
}
async function main() {
  const opts = parseDoctorOptions();
  const projectDir = process.cwd();
  const result = await runDoctor(projectDir, { deep: opts.deep });
  if (opts.json) {
    console.log(formatDoctorJson(result));
  } else {
    console.log(formatDoctorOutput(result));
  }
  process.exit(result.exitCode);
}
if (import.meta.main) {
  void runCLI(main);
}

export {
  parseDoctorOptions,
  runDoctor,
  runDoctorForInit,
  formatDoctorOutput,
  formatDoctorJson,
  main
};
