// src/actions/lintTierAnalysis.ts
import { readFileSync } from "node:fs";
import { createRequire } from "node:module";
import path from "node:path";
import { pathToFileURL } from "node:url";
import chalk from "chalk";
import { findUpSync } from "find-up";
var tierExportPattern = /config(?:React)?Tier\d+[A-Za-z]*/g;
var composedConfigModules = [
  "bestPracticesCoreConfig",
  "consistencyRulesConfig",
  "correctnessCoreConfig",
  "importConfig",
  "rulesConfig",
  "typescriptConfig",
  "typescriptConsistencyConfig",
  "typescriptCorrectnessConfig",
  "typescriptTypeCheckedConfig",
  "typescriptTypeCheckedConsistencyConfig",
  "typescriptTypeCheckedCorrectnessConfig",
  "unicornConfig",
  "workspacesConfig"
];
function escapeRegExp(value) {
  return value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
}
function hasComposedConfigModules(source) {
  return composedConfigModules.some((module) => source.includes(module));
}
function normalizeRuleLevel(setting) {
  let current = setting;
  while (Array.isArray(current) && current.length > 0) {
    current = current[0];
  }
  if (current === void 0) return "off";
  if (current === "off" || current === 0) return "off";
  if (current === "warn" || current === 1) return "warn";
  if (current === "error" || current === 2) return "error";
  return "off";
}
function countActualLintMessages(messages) {
  let errors = 0;
  let warnings = 0;
  for (const message of messages) {
    if (message.severity === 2) errors++;
    else warnings++;
  }
  return { errors, warnings };
}
function countMessagesForTier(messages, rules) {
  let errors = 0;
  let warnings = 0;
  for (const message of messages) {
    if (message.ruleId === null) {
      errors++;
      continue;
    }
    const level = normalizeRuleLevel(rules[message.ruleId]);
    if (level === "off") continue;
    if (level === "error") errors++;
    else warnings++;
  }
  return { errors, warnings };
}
function resolveLintTierConfigKind(source, currentTierExport) {
  if (currentTierExport === void 0) return "custom";
  const presetExportPattern = RegExp(String.raw`export\s+default\s+${escapeRegExp(currentTierExport)}\b`);
  if (presetExportPattern.test(source)) return "preset";
  if (hasComposedConfigModules(source)) return "custom";
  if (source.includes(`...${currentTierExport}`)) return "preset";
  return "custom";
}
function getLintTierDefinitions(isReact, isTypeChecked) {
  if (isReact) {
    return [
      {
        id: 0,
        label: "Correctness",
        exportName: isTypeChecked ? "configReactTier0CorrectnessTypeChecked" : "configReactTier0Correctness"
      },
      {
        id: 1,
        label: "Consistency",
        exportName: isTypeChecked ? "configReactTier1ConsistencyTypeChecked" : "configReactTier1Consistency"
      },
      {
        id: 2,
        label: "Best Practices",
        exportName: isTypeChecked ? "configReactTier2BestPracticesTypeChecked" : "configReactTier2BestPractices"
      },
      {
        id: 3,
        label: "Opinionated",
        exportName: "configReactTier3OpinionatedTypeChecked"
      },
      {
        id: 4,
        label: "Experimental",
        exportName: isTypeChecked ? "configReactTier4ExperimentalTypeChecked" : "configReactTier4Experimental"
      }
    ];
  }
  return [
    {
      id: 0,
      label: "Correctness",
      exportName: isTypeChecked ? "configTier0CorrectnessTypeChecked" : "configTier0Correctness"
    },
    {
      id: 1,
      label: "Consistency",
      exportName: isTypeChecked ? "configTier1ConsistencyTypeChecked" : "configTier1Consistency"
    },
    {
      id: 2,
      label: "Best Practices",
      exportName: isTypeChecked ? "configTier2BestPracticesTypeChecked" : "configTier2BestPractices"
    },
    {
      id: 3,
      label: "Opinionated",
      exportName: isTypeChecked ? "configTier3OpinionatedTypeChecked" : "configTier3Opinionated"
    },
    {
      id: 4,
      label: "Experimental",
      exportName: isTypeChecked ? "configTier4ExperimentalTypeChecked" : "configTier4Experimental"
    }
  ];
}
function detectLintTierContext(cwd) {
  const configPath = findUpSync("eslint.config.ts", { cwd });
  if (configPath === void 0) {
    return {
      configKind: "custom",
      hasXylabsConfig: false,
      isReact: false,
      isTypeChecked: true
    };
  }
  const source = readFileSync(configPath, "utf8");
  const hasXylabsConfig = source.includes("@xylabs/eslint-config-flat") || source.includes("@xylabs/eslint-config-react-flat");
  const isReact = source.includes("@xylabs/eslint-config-react-flat");
  const isTypeChecked = /(?:config(?:React)?Tier\d+\w*TypeChecked|typescriptTypeCheckedConfig)/.test(source);
  const tierMatches = source.match(tierExportPattern) ?? [];
  const currentTierExport = tierMatches.at(-1);
  const configKind = resolveLintTierConfigKind(source, currentTierExport);
  return {
    configKind,
    currentTierExport,
    hasXylabsConfig,
    isReact,
    isTypeChecked
  };
}
function findClosestTierPreset(actual, counts) {
  if (counts.length === 0) return void 0;
  let closest = counts[0];
  let closestDistance = Infinity;
  for (const entry of counts) {
    const distance = Math.abs(entry.errors - actual.errors) + Math.abs(entry.warnings - actual.warnings);
    if (distance < closestDistance) {
      closest = entry;
      closestDistance = distance;
    }
  }
  return closest?.tier;
}
async function loadXylabsEslintConfigPackage(cwd, isReact) {
  const packageName = isReact ? "@xylabs/eslint-config-react-flat" : "@xylabs/eslint-config-flat";
  const requireFromCwd = createRequire(path.join(cwd, "package.json"));
  const resolved = requireFromCwd.resolve(packageName);
  return import(pathToFileURL(resolved).href);
}
function resolveTierConfigExport(configPackage, exportName) {
  const configs = configPackage[exportName];
  if (!Array.isArray(configs)) {
    throw new TypeError(`Missing ESLint tier export "${exportName}" from @xylabs/eslint-config`);
  }
  return configs;
}
function mergeTierRules(mergeConfigRules, configs) {
  return mergeConfigRules(configs);
}
async function loadMergeConfigRules(cwd) {
  const flatPackage = await loadXylabsEslintConfigPackage(cwd, false);
  const mergeConfigRules = flatPackage.mergeConfigRules;
  if (typeof mergeConfigRules !== "function") {
    throw new TypeError("mergeConfigRules is missing from @xylabs/eslint-config-flat");
  }
  return mergeConfigRules;
}
async function buildLintTierRuleMaps(cwd, context) {
  const configPackage = await loadXylabsEslintConfigPackage(cwd, context.isReact);
  const mergeConfigRules = await loadMergeConfigRules(cwd);
  const definitions = getLintTierDefinitions(context.isReact, context.isTypeChecked);
  const maps = /* @__PURE__ */ new Map();
  for (const definition of definitions) {
    const configs = resolveTierConfigExport(configPackage, definition.exportName);
    maps.set(definition.id, mergeTierRules(mergeConfigRules, configs));
  }
  return maps;
}
function resolveMaxTierConfigExport(context) {
  return getLintTierDefinitions(context.isReact, context.isTypeChecked).at(-1)?.exportName ?? "configTier4ExperimentalTypeChecked";
}
function projectLintTierCounts(messages, tierRuleMaps, definitions) {
  return definitions.map((definition) => {
    const rules = tierRuleMaps.get(definition.id) ?? {};
    const { errors, warnings } = countMessagesForTier(messages, rules);
    return {
      errors,
      label: definition.label,
      tier: definition.id,
      warnings
    };
  });
}
function formatLintTierAnalysisReport({
  actual,
  context,
  counts,
  currentTierExport,
  files,
  timeMs
}) {
  const definitions = getLintTierDefinitions(context.isReact, context.isTypeChecked);
  const currentTierId = definitions.find((tier) => tier.exportName === currentTierExport)?.id;
  const closestTierId = context.configKind === "custom" ? findClosestTierPreset(actual, counts) : currentTierId;
  const profile = [
    context.isReact ? "react" : "flat",
    context.isTypeChecked ? "type-checked" : "untyped"
  ].join(", ");
  const labelWidth = Math.max(...counts.map((entry) => entry.label.length), "Experimental".length);
  const lines = [
    chalk.cyan("Lint tier analysis"),
    chalk.gray(`XY preset projection (${profile}); based on your eslint.config.ts with no extra ESLint passes`),
    chalk.white(`Your lint run: ${actual.errors} errors, ${actual.warnings} warnings`),
    "",
    chalk.gray("If you switched to each XY tier preset:"),
    ` ${"Tier".padEnd(4)} ${"Preset".padEnd(labelWidth)}  ${"Errors".padStart(6)}  ${"Warnings".padStart(8)}`,
    ` ${"-".repeat(4)} ${"-".repeat(labelWidth)}  ${"-".repeat(6)}  ${"-".repeat(8)}`
  ];
  for (const entry of counts) {
    const isCurrentPreset = context.configKind === "preset" && entry.tier === currentTierId;
    const isClosestPreset = context.configKind === "custom" && entry.tier === closestTierId;
    const marker = isCurrentPreset ? chalk.gray("  <- current preset") : isClosestPreset ? chalk.gray("  <- closest preset") : "";
    lines.push(
      ` ${String(entry.tier).padEnd(4)} ${entry.label.padEnd(labelWidth)}  ${String(entry.errors).padStart(6)}  ${String(entry.warnings).padStart(8)}${marker}`
    );
  }
  if (context.configKind === "custom") {
    lines.push(
      "",
      chalk.yellow("Note: eslint.config.ts composes XY modules directly instead of exporting a single tier preset. Higher-tier error counts promote current warnings to errors.")
    );
  } else if (currentTierId !== void 0 && currentTierId < 4) {
    lines.push(
      "",
      chalk.yellow("Note: eslint.config.ts is below tier 4. Counts for higher tiers only include violations ESLint reported with your current config and may be understated.")
    );
  }
  lines.push(
    "",
    chalk.gray(`Analyzed ${files} files in ${timeMs < 1e3 ? `${timeMs}ms` : `${(timeMs / 1e3).toFixed(2)}s`}`)
  );
  return lines.join("\n");
}
export {
  buildLintTierRuleMaps,
  countActualLintMessages,
  countMessagesForTier,
  detectLintTierContext,
  findClosestTierPreset,
  formatLintTierAnalysisReport,
  getLintTierDefinitions,
  loadMergeConfigRules,
  loadXylabsEslintConfigPackage,
  mergeTierRules,
  normalizeRuleLevel,
  projectLintTierCounts,
  resolveLintTierConfigKind,
  resolveMaxTierConfigExport,
  resolveTierConfigExport
};
//# sourceMappingURL=lintTierAnalysis.mjs.map
