import {
  INLINE_KEYWORD_MAX_OFFSET
} from "./chunk-2GXVIN57.js";
import {
  extractParent
} from "./chunk-JONWQUB5.js";
import {
  getGitForCwd
} from "./chunk-2UFQ3A3C.js";
import {
  BRANCHES,
  EMOJI,
  GIT_COMMANDS,
  LANE_PATH_PATTERNS,
  LOG_PREFIX,
  STRING_LITERALS
} from "./chunk-DWMLTXKQ.js";
import {
  ErrorCodes,
  createError
} from "./chunk-RXRKBBSM.js";

// ../core/dist/code-path-validator.js
import path from "path";
import { existsSync, readFileSync } from "fs";
import { execSync } from "child_process";
import micromatch from "micromatch";
var VALIDATION_MODES = Object.freeze({
  /** Check file existence - used by wu:done */
  EXIST: "exist",
  /** Check lane pattern matching - used by wu:claim */
  LANE: "lane",
  /** Check code quality (TODOs, mocks) - used by wu:done */
  QUALITY: "quality"
});
function isTestFile(filePath) {
  const normalized = filePath.replace(/\\/g, "/");
  const testPatterns = [
    /\.test\.(ts|tsx|js|jsx|mjs)$/,
    /\.spec\.(ts|tsx|js|jsx|mjs)$/,
    /__tests__\//,
    /\.test-utils\./,
    /\.mock\./
  ];
  return testPatterns.some((pattern) => pattern.test(normalized));
}
function isMarkdownFile(filePath) {
  const normalized = filePath.replace(/\\/g, "/");
  return /\.md$/i.test(normalized);
}
function getRepoRoot() {
  try {
    return execSync("git rev-parse --show-toplevel", {
      encoding: "utf-8",
      stdio: ["pipe", "pipe", "ignore"]
    }).trim();
  } catch {
    return process.cwd();
  }
}
function validateExistenceInWorktree(codePaths, worktreePath) {
  const missing = [];
  const errors = [];
  for (const filePath of codePaths) {
    const fullPath = path.join(worktreePath, filePath);
    if (!existsSync(fullPath)) {
      missing.push(filePath);
    }
  }
  if (missing.length > 0) {
    errors.push(`code_paths validation failed - ${missing.length} file(s) not found in worktree:
${missing.map((p) => `  - ${p}`).join(STRING_LITERALS.NEWLINE)}

Ensure all files listed in code_paths exist before running wu:done.`);
  }
  return { valid: errors.length === 0, errors, missing };
}
async function validateExistenceOnBranch(codePaths, targetBranch) {
  const missing = [];
  const errors = [];
  try {
    const gitAdapter = getGitForCwd();
    for (const filePath of codePaths) {
      try {
        const result = await gitAdapter.raw([GIT_COMMANDS.LS_TREE, targetBranch, "--", filePath]);
        if (!result || result.trim() === "") {
          missing.push(filePath);
        }
      } catch {
        missing.push(filePath);
      }
    }
    if (missing.length > 0) {
      errors.push(`code_paths validation failed - ${missing.length} file(s) not found on ${targetBranch}:
${missing.map((p) => `  - ${p}`).join(STRING_LITERALS.NEWLINE)}

\u274C POTENTIAL FALSE COMPLETION DETECTED

These files are listed in code_paths but do not exist on ${targetBranch}.
This prevents creating a stamp for incomplete work.

Fix options:
  1. Ensure all code is committed and merged to ${targetBranch}
  2. Update code_paths in WU YAML to match actual files
  3. Remove files that were intentionally not created

Context: WU-1351 prevents false completions from INIT-WORKFLOW-INTEGRITY`);
    }
  } catch (err) {
    const errMessage = err instanceof Error ? err.message : String(err);
    console.warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} Could not validate code_paths: ${errMessage}`);
    return { valid: true, errors: [], missing: [] };
  }
  return { valid: errors.length === 0, errors, missing };
}
function validateLanePatterns(codePaths, lane) {
  if (!codePaths || codePaths.length === 0) {
    return {
      hasWarnings: false,
      warnings: [],
      violations: [],
      skipped: true
    };
  }
  const parentLane = extractParent(lane);
  const patterns = LANE_PATH_PATTERNS[parentLane];
  if (!patterns) {
    return {
      hasWarnings: false,
      warnings: [],
      violations: [],
      skipped: true
    };
  }
  const { exclude = [], allowExceptions = [] } = patterns;
  const violations = codePaths.filter((codePath) => {
    const matchesExclude = micromatch.isMatch(codePath, exclude, { nocase: true });
    if (!matchesExclude)
      return false;
    if (allowExceptions.length > 0) {
      const matchesException = micromatch.isMatch(codePath, allowExceptions, { nocase: true });
      if (matchesException)
        return false;
    }
    return true;
  });
  if (violations.length === 0) {
    return {
      hasWarnings: false,
      warnings: [],
      violations: [],
      skipped: false
    };
  }
  const warnings = violations.map((violatingPath) => {
    return `Lane "${lane}" typically doesn't include "${violatingPath}" (expected for different lane)`;
  });
  return {
    hasWarnings: true,
    warnings,
    violations,
    skipped: false
  };
}
function scanFileForTODOs(filePath) {
  if (!existsSync(filePath)) {
    return { found: false, matches: [] };
  }
  if (isTestFile(filePath)) {
    return { found: false, matches: [] };
  }
  if (isMarkdownFile(filePath)) {
    return { found: false, matches: [] };
  }
  try {
    const content = readFileSync(filePath, { encoding: "utf-8" });
    const lines = content.split(/\r?\n/);
    const matches = [];
    const checkForActionableMarker = (line) => {
      const trimmed = line.trim();
      if (trimmed.includes("// TODO:,") || trimmed.includes("/* TODO */")) {
        return { found: false, pattern: null };
      }
      if (trimmed.includes("@todo,") || trimmed.includes("@-prefixed:")) {
        return { found: false, pattern: null };
      }
      const atTagMatch = trimmed.match(/^\*\s+@(todo|fixme|hack|xxx)\b/i);
      if (atTagMatch) {
        const atTag = atTagMatch[1];
        return atTag ? { found: true, pattern: atTag.toUpperCase() } : { found: false, pattern: null };
      }
      const commentStartMatch = trimmed.match(/^(?:\/\/|\/\*+|\*|<!--|#)\s*(TODO|FIXME|HACK|XXX)(?::|[\s]|$)/i);
      if (commentStartMatch) {
        const commentKeyword = commentStartMatch[1];
        if (!commentKeyword) {
          return { found: false, pattern: null };
        }
        const afterKeyword = trimmed.slice(trimmed.indexOf(commentKeyword) + commentKeyword.length);
        if (!afterKeyword.startsWith("/")) {
          return { found: true, pattern: commentKeyword.toUpperCase() };
        }
      }
      const inlineCommentMatch = line.match(/\/\/\s*(TODO|FIXME|HACK|XXX)(?::|[\s]|$)/i);
      if (inlineCommentMatch && !line.match(/\/\/\s*(TODO|FIXME|HACK|XXX)\//i)) {
        const inlineKeyword = inlineCommentMatch[1];
        if (!inlineKeyword) {
          return { found: false, pattern: null };
        }
        const doubleSlashIndex = line.indexOf("//");
        const beforeSlash = line.slice(0, doubleSlashIndex);
        const singleQuotes = (beforeSlash.match(/(?<!\\)'/g) || []).length;
        const doubleQuotes = (beforeSlash.match(/(?<!\\)"/g) || []).length;
        const backticks = (beforeSlash.match(/(?<!\\)`/g) || []).length;
        if (singleQuotes % 2 !== 0 || doubleQuotes % 2 !== 0 || backticks % 2 !== 0) {
          return { found: false, pattern: null };
        }
        const commentPart = line.slice(doubleSlashIndex);
        const keywordIndex = commentPart.search(/\b(TODO|FIXME|HACK|XXX)\b/i);
        if (keywordIndex >= 0 && keywordIndex <= INLINE_KEYWORD_MAX_OFFSET) {
          return { found: true, pattern: inlineKeyword.toUpperCase() };
        }
      }
      if (trimmed.match(/\bWU-XXX\b/i)) {
        return { found: false, pattern: null };
      }
      return { found: false, pattern: null };
    };
    lines.forEach((line, index) => {
      const lineNumber = index + 1;
      const trimmed = line.trim();
      const isComment = /^(\/\/|\/\*|\*|<!--|#)/.test(trimmed) || line.includes("//") || line.includes("/*");
      if (isComment) {
        const result = checkForActionableMarker(line);
        if (result.found) {
          matches.push({
            line: lineNumber,
            text: trimmed,
            pattern: result.pattern
          });
        }
      }
    });
    return { found: matches.length > 0, matches };
  } catch {
    return { found: false, matches: [] };
  }
}
function scanFileForMocks(filePath) {
  if (!existsSync(filePath)) {
    return { found: false, matches: [] };
  }
  if (isTestFile(filePath)) {
    return { found: false, matches: [] };
  }
  try {
    const content = readFileSync(filePath, { encoding: "utf-8" });
    const lines = content.split(/\r?\n/);
    const matches = [];
    const mockPatterns = [
      { name: "Mock", regex: /\b(class|export\s+class)\s+(\w*Mock\w*)/i },
      { name: "Stub", regex: /\b(class|export\s+class)\s+(\w*Stub\w*)/i },
      { name: "Fake", regex: /\b(class|export\s+class)\s+(\w*Fake\w*)/i },
      { name: "Placeholder", regex: /\b(class|export\s+class)\s+(\w*Placeholder\w*)/i },
      { name: "Mock", regex: /\b(function|const|let|var)\s+(\w*mock\w*)/i },
      { name: "Stub", regex: /\b(function|const|let|var)\s+(\w*stub\w*)/i },
      { name: "Fake", regex: /\b(function|const|let|var)\s+(\w*fake\w*)/i },
      { name: "Placeholder", regex: /\b(function|const|let|var)\s+(\w*placeholder\w*)/i }
    ];
    lines.forEach((line, index) => {
      const lineNumber = index + 1;
      mockPatterns.forEach(({ name, regex }) => {
        const match = regex.exec(line);
        if (match) {
          matches.push({
            line: lineNumber,
            text: line.trim(),
            type: name
          });
        }
      });
    });
    return { found: matches.length > 0, matches };
  } catch {
    return { found: false, matches: [] };
  }
}
function formatTODOFindings(findings) {
  let msg = "\n\u274C TODO/FIXME/HACK/XXX comments found in production code:\n";
  findings.forEach(({ path: filePath, matches }) => {
    msg += `
  ${filePath}:
`;
    matches.forEach(({ line, text }) => {
      msg += `    Line ${line}: ${text}
`;
    });
  });
  msg += "\nThese indicate incomplete work and must be resolved before WU completion.";
  msg += "\nEither complete the work or use --allow-todo with justification in WU notes.";
  return msg;
}
function formatMockFindings(findings) {
  let msg = "\n\u26A0\uFE0F  Mock/Stub/Fake/Placeholder classes found in production code:\n";
  findings.forEach(({ path: filePath, matches }) => {
    msg += `
  ${filePath}:
`;
    matches.forEach(({ line, text }) => {
      msg += `    Line ${line}: ${text}
`;
    });
  });
  msg += "\nThese suggest incomplete implementation (interface \u2260 implementation).";
  msg += "\nVerify these are actual implementations, not placeholder code.";
  return msg;
}
function validateCodeQuality(codePaths, options = {}) {
  const { allowTodos = false, worktreePath = null } = options;
  const errors = [];
  const warnings = [];
  const repoRoot = worktreePath || getRepoRoot();
  if (!codePaths || codePaths.length === 0) {
    return { valid: true, errors, warnings };
  }
  const todoFindings = [];
  const mockFindings = [];
  for (const codePath of codePaths) {
    const absolutePath = path.join(repoRoot, codePath);
    if (!existsSync(absolutePath)) {
      errors.push(`
\u274C Code path validation failed: File does not exist: ${codePath}

This indicates the WU claims to have created/modified a file that doesn't exist.
Either create the file, or remove it from code_paths in the WU YAML.
`);
      continue;
    }
    const todoResult = scanFileForTODOs(absolutePath);
    if (todoResult.found) {
      todoFindings.push({ path: codePath, ...todoResult });
    }
    const mockResult = scanFileForMocks(absolutePath);
    if (mockResult.found) {
      mockFindings.push({ path: codePath, ...mockResult });
    }
  }
  if (todoFindings.length > 0) {
    const message = formatTODOFindings(todoFindings);
    if (allowTodos) {
      warnings.push(message);
    } else {
      errors.push(message);
    }
  }
  if (mockFindings.length > 0) {
    warnings.push(formatMockFindings(mockFindings));
  }
  return { valid: errors.length === 0, errors, warnings };
}
async function validate(codePaths, options = {}) {
  const { mode = VALIDATION_MODES.EXIST } = options;
  switch (mode) {
    case VALIDATION_MODES.EXIST: {
      const { worktreePath, targetBranch = BRANCHES.MAIN } = options;
      if (!codePaths || codePaths.length === 0) {
        return { valid: true, errors: [], missing: [] };
      }
      if (worktreePath && existsSync(worktreePath)) {
        return validateExistenceInWorktree(codePaths, worktreePath);
      }
      return validateExistenceOnBranch(codePaths, targetBranch);
    }
    case VALIDATION_MODES.LANE: {
      const { lane } = options;
      if (!lane) {
        throw createError(ErrorCodes.INVALID_ARGUMENT, "Lane name is required for lane validation mode");
      }
      return validateLanePatterns(codePaths, lane);
    }
    case VALIDATION_MODES.QUALITY: {
      const { worktreePath, allowTodos } = options;
      return validateCodeQuality(codePaths, { worktreePath, allowTodos });
    }
    default:
      throw createError(ErrorCodes.INVALID_ARGUMENT, `Unknown validation mode: ${mode}`);
  }
}
async function validateCodePathsExist(doc, _id, options = {}) {
  const codePaths = doc.code_paths || [];
  const { targetBranch = BRANCHES.MAIN, worktreePath = null } = options;
  if (codePaths.length === 0) {
    console.log(`${LOG_PREFIX.DONE} ${EMOJI.INFO} No code_paths to validate for ${_id}`);
    return { valid: true, errors: [], missing: [] };
  }
  console.log(`${LOG_PREFIX.DONE} Validating ${codePaths.length} code_paths exist...`);
  const result = await validate(codePaths, {
    mode: VALIDATION_MODES.EXIST,
    worktreePath: worktreePath ?? void 0,
    targetBranch
  });
  if (result.valid) {
    console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} All ${codePaths.length} code_paths verified`);
  }
  return result;
}
function validateLaneCodePaths(doc, lane) {
  const codePaths = doc.code_paths || [];
  return validateLanePatterns(codePaths, lane);
}
function validateWUCodePaths(codePaths, options = {}) {
  const { allowTodos = false, worktreePath = null } = options;
  return validateCodeQuality(codePaths, { worktreePath, allowTodos });
}
function logLaneValidationWarnings(result, logPrefix = "[wu-claim]") {
  if (!result.hasWarnings) {
    return;
  }
  console.warn(`${logPrefix} Lane/code_paths mismatch detected (advisory only):`);
  for (const warning of result.warnings) {
    console.warn(`${logPrefix}   ${warning}`);
  }
  console.warn(`${logPrefix} This is a warning only - proceeding with claim.`);
}

export {
  validateCodePathsExist,
  validateLaneCodePaths,
  validateWUCodePaths,
  logLaneValidationWarnings
};
