/**
 * 编辑验证器
 *
 * 在应用编辑之前验证编辑的有效性和安全性
 */

import * as fs from 'fs/promises';
import type {
  Edit,
  WholeFileEdit,
  SearchReplaceEdit,
  DiffEdit,
  EditValidationResult,
  EditValidationError,
  EditValidationWarning,
  SearchReplaceBlock,
} from './types.js';
import { findSearchPositions, normalizeSearchString } from './parsers.js';

/**
 * 验证编辑操作
 */
export async function validateEdit(edit: Edit): Promise<EditValidationResult> {
  const errors: EditValidationError[] = [];
  const warnings: EditValidationWarning[] = [];

  // 检查文件是否存在（对于非新建文件的操作）
  let fileContent: string | null = null;
  let fileExists = false;

  try {
    fileContent = await fs.readFile(edit.filePath, 'utf-8');
    fileExists = true;
  } catch {
    // 文件不存在
  }

  // 根据编辑模式进行验证
  switch (edit.mode) {
    case 'whole':
      validateWholeFileEdit(edit, fileContent, fileExists, errors, warnings);
      break;
    case 'search-replace':
      validateSearchReplaceEdit(edit, fileContent, fileExists, errors, warnings);
      break;
    case 'diff':
      validateDiffEdit(edit, fileContent, fileExists, errors, warnings);
      break;
  }

  return {
    valid: errors.length === 0,
    errors,
    warnings,
  };
}

/**
 * 验证整文件替换编辑
 */
function validateWholeFileEdit(
  edit: WholeFileEdit,
  fileContent: string | null,
  _fileExists: boolean,
  errors: EditValidationError[],
  warnings: EditValidationWarning[]
): void {
  // 检查是否是二进制文件
  if (fileContent !== null && isBinaryContent(fileContent)) {
    warnings.push({
      type: 'binary',
      message: '目标文件可能是二进制文件',
    });
  }

  // 检查变更幅度
  if (fileContent !== null) {
    const changeRatio = Math.abs(edit.content.length - fileContent.length) / fileContent.length;
    if (changeRatio > 0.8 && fileContent.length > 1000) {
      warnings.push({
        type: 'large_change',
        message: `文件内容变化较大 (${Math.round(changeRatio * 100)}%)，请确认是否正确`,
      });
    }
  }

  // 检查空白字符问题
  if (edit.content.includes('\t') && fileContent?.includes('    ')) {
    warnings.push({
      type: 'whitespace',
      message: '新内容使用 Tab 缩进，但原文件使用空格缩进',
    });
  } else if (edit.content.includes('    ') && fileContent?.includes('\t')) {
    warnings.push({
      type: 'whitespace',
      message: '新内容使用空格缩进，但原文件使用 Tab 缩进',
    });
  }
}

/**
 * 验证搜索替换编辑
 */
function validateSearchReplaceEdit(
  edit: SearchReplaceEdit,
  fileContent: string | null,
  fileExists: boolean,
  errors: EditValidationError[],
  warnings: EditValidationWarning[]
): void {
  // 文件必须存在
  if (!fileExists || fileContent === null) {
    errors.push({
      type: 'not_found',
      message: `文件不存在: ${edit.filePath}`,
    });
    return;
  }

  // 验证每个搜索替换块
  for (const block of edit.blocks) {
    validateSearchReplaceBlock(block, fileContent, errors, warnings);
  }

  // 检查是否有重叠的搜索区域
  checkOverlappingBlocks(edit.blocks, fileContent, errors);
}

/**
 * 验证单个搜索替换块
 */
function validateSearchReplaceBlock(
  block: SearchReplaceBlock,
  fileContent: string,
  errors: EditValidationError[],
  warnings: EditValidationWarning[]
): void {
  const { search, replace } = block;

  // 检查搜索字符串是否为空
  if (!search || search.length === 0) {
    errors.push({
      type: 'syntax',
      message: '搜索字符串不能为空',
      search,
    });
    return;
  }

  // 查找匹配
  const positions = findSearchPositions(fileContent, search);

  if (positions.length === 0) {
    // 尝试规范化后再查找
    const normalizedSearch = normalizeSearchString(search, {
      trimTrailingWhitespace: true,
      normalizeLineEndings: true,
    });
    const normalizedContent = normalizeSearchString(fileContent, {
      trimTrailingWhitespace: true,
      normalizeLineEndings: true,
    });

    const normalizedPositions = findSearchPositions(normalizedContent, normalizedSearch);

    if (normalizedPositions.length > 0) {
      warnings.push({
        type: 'whitespace',
        message: '搜索字符串与文件内容存在空白字符差异，已自动处理',
      });
    } else {
      // 提供更友好的错误信息
      const similarMatch = findSimilarMatch(fileContent, search);
      let errorMessage = `未找到要替换的内容`;
      if (similarMatch) {
        errorMessage += `。找到相似内容在第 ${similarMatch.line} 行`;
      }

      errors.push({
        type: 'not_found',
        message: errorMessage,
        search: search.length > 100 ? search.slice(0, 100) + '...' : search,
        occurrences: 0,
      });
    }
    return;
  }

  if (positions.length > 1) {
    // 找到多个匹配
    const lineNumbers = positions.map(pos =>
      fileContent.slice(0, pos).split('\n').length
    );

    errors.push({
      type: 'ambiguous',
      message: `找到 ${positions.length} 处匹配（行 ${lineNumbers.join(', ')}）。请提供更多上下文使搜索字符串唯一`,
      search: search.length > 100 ? search.slice(0, 100) + '...' : search,
      occurrences: positions.length,
    });
    return;
  }

  // 检查替换后是否有意义
  if (search === replace) {
    warnings.push({
      type: 'whitespace',
      message: '搜索和替换内容相同，此操作无效',
    });
  }
}

/**
 * 验证 Diff 编辑
 */
function validateDiffEdit(
  edit: DiffEdit,
  fileContent: string | null,
  fileExists: boolean,
  errors: EditValidationError[],
  _warnings: EditValidationWarning[]
): void {
  // 文件必须存在（除非是新建文件的 diff）
  if (!fileExists && !edit.patch.includes('new file mode')) {
    errors.push({
      type: 'not_found',
      message: `文件不存在: ${edit.filePath}`,
    });
    return;
  }

  // 验证 diff 格式
  if (!edit.patch.includes('@@') && !edit.patch.includes('diff --git')) {
    errors.push({
      type: 'syntax',
      message: 'Diff 格式无效，缺少 hunk 头部 (@@) 或 diff 头部',
    });
    return;
  }

  // 验证上下文行是否匹配
  if (fileContent) {
    const contextErrors = validateDiffContext(edit.patch, fileContent);
    errors.push(...contextErrors);
  }
}

/**
 * 验证 diff 中的上下文行是否与文件内容匹配
 */
function validateDiffContext(patch: string, fileContent: string): EditValidationError[] {
  const errors: EditValidationError[] = [];
  const fileLines = fileContent.split('\n');
  const patchLines = patch.split('\n');

  let currentHunkOldStart = 0;
  let oldLineOffset = 0;

  for (const line of patchLines) {
    // 解析 hunk 头部
    const hunkMatch = line.match(/@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@/);
    if (hunkMatch) {
      currentHunkOldStart = parseInt(hunkMatch[1], 10);
      oldLineOffset = 0;
      continue;
    }

    // 检查上下文行
    if (line.startsWith(' ')) {
      const contextContent = line.slice(1);
      const fileLineIndex = currentHunkOldStart - 1 + oldLineOffset;

      if (fileLineIndex < fileLines.length) {
        if (fileLines[fileLineIndex] !== contextContent) {
          errors.push({
            type: 'conflict',
            message: `第 ${fileLineIndex + 1} 行的上下文不匹配`,
            line: fileLineIndex + 1,
          });
        }
      }
      oldLineOffset++;
    } else if (line.startsWith('-')) {
      oldLineOffset++;
    }
  }

  return errors;
}

/**
 * 检查搜索块是否有重叠
 */
function checkOverlappingBlocks(
  blocks: SearchReplaceBlock[],
  fileContent: string,
  errors: EditValidationError[]
): void {
  const ranges: Array<{ start: number; end: number; index: number }> = [];

  for (let i = 0; i < blocks.length; i++) {
    const positions = findSearchPositions(fileContent, blocks[i].search);
    for (const pos of positions) {
      ranges.push({
        start: pos,
        end: pos + blocks[i].search.length,
        index: i,
      });
    }
  }

  // 检查重叠
  ranges.sort((a, b) => a.start - b.start);

  for (let i = 0; i < ranges.length - 1; i++) {
    if (ranges[i].end > ranges[i + 1].start && ranges[i].index !== ranges[i + 1].index) {
      errors.push({
        type: 'conflict',
        message: `搜索块 ${ranges[i].index + 1} 和 ${ranges[i + 1].index + 1} 存在重叠区域`,
      });
    }
  }
}

/**
 * 检测是否是二进制内容
 */
function isBinaryContent(content: string): boolean {
  // 检查是否包含 null 字符或大量不可打印字符
  const nonPrintableCount = (content.match(/[\x00-\x08\x0B\x0C\x0E-\x1F]/g) || []).length;
  return nonPrintableCount > content.length * 0.1;
}

/**
 * 查找相似匹配（用于错误提示）
 */
function findSimilarMatch(
  content: string,
  search: string
): { line: number; similarity: number } | null {
  // 取搜索字符串的第一行作为关键内容
  const firstLine = search.split('\n')[0].trim();
  if (firstLine.length < 5) {
    return null;
  }

  const lines = content.split('\n');
  let bestMatch: { line: number; similarity: number } | null = null;

  for (let i = 0; i < lines.length; i++) {
    const line = lines[i];
    if (line.includes(firstLine.slice(0, Math.min(20, firstLine.length)))) {
      const similarity = calculateSimilarity(line, firstLine);
      if (!bestMatch || similarity > bestMatch.similarity) {
        bestMatch = { line: i + 1, similarity };
      }
    }
  }

  return bestMatch && bestMatch.similarity > 0.5 ? bestMatch : null;
}

/**
 * 计算字符串相似度（简单实现）
 */
function calculateSimilarity(a: string, b: string): number {
  if (a === b) return 1;
  if (a.length === 0 || b.length === 0) return 0;

  const longer = a.length > b.length ? a : b;
  const shorter = a.length > b.length ? b : a;

  const longerLength = longer.length;
  if (longerLength === 0) return 1;

  // 简单的包含检查
  if (longer.includes(shorter)) {
    return shorter.length / longerLength;
  }

  // 计算共同字符数
  let matches = 0;
  const shorterChars = shorter.split('');
  const longerChars = longer.split('');

  for (const char of shorterChars) {
    const idx = longerChars.indexOf(char);
    if (idx !== -1) {
      matches++;
      longerChars.splice(idx, 1);
    }
  }

  return matches / longerLength;
}

/**
 * 批量验证编辑
 */
export async function validateEdits(edits: Edit[]): Promise<EditValidationResult[]> {
  return Promise.all(edits.map(edit => validateEdit(edit)));
}

/**
 * 检查所有编辑是否都有效
 */
export async function areAllEditsValid(edits: Edit[]): Promise<{
  valid: boolean;
  results: EditValidationResult[];
}> {
  const results = await validateEdits(edits);
  const valid = results.every(r => r.valid);
  return { valid, results };
}
