/**
 * 编辑应用器
 *
 * 将编辑操作应用到文件
 */

import * as fs from 'fs/promises';
import * as path from 'path';
import type {
  Edit,
  WholeFileEdit,
  SearchReplaceEdit,
  DiffEdit,
  EditApplyResult,
  EditStats,
  BatchEdit,
  BatchEditResult,
} from './types.js';
import { validateEdit } from './validator.js';
import { applyDiffPatch, normalizeSearchString, findSearchPositions } from './parsers.js';
import { touchFile, getFormattedFileDiagnostics, isLanguageSupported } from '../lsp/index.js';

/**
 * 应用编辑选项
 */
export interface ApplyEditOptions {
  /** 是否在应用前验证 */
  validate?: boolean;
  /** 是否创建备份 */
  backup?: boolean;
  /** 备份目录 */
  backupDir?: string;
  /** 是否运行 LSP 诊断 */
  runDiagnostics?: boolean;
  /** 是否为试运行（不实际写入） */
  dryRun?: boolean;
}

const DEFAULT_OPTIONS: ApplyEditOptions = {
  validate: true,
  backup: false,
  runDiagnostics: true,
  dryRun: false,
};

/**
 * 应用单个编辑
 */
export async function applyEdit(
  edit: Edit,
  options: ApplyEditOptions = {}
): Promise<EditApplyResult> {
  const opts = { ...DEFAULT_OPTIONS, ...options };

  // 验证编辑
  if (opts.validate) {
    const validation = await validateEdit(edit);
    if (!validation.valid) {
      const errorMessages = validation.errors.map(e => e.message).join('; ');
      return {
        success: false,
        filePath: edit.filePath,
        error: `验证失败: ${errorMessages}`,
      };
    }
  }

  // 读取原始内容
  let originalContent: string | null = null;
  try {
    originalContent = await fs.readFile(edit.filePath, 'utf-8');
  } catch {
    // 文件不存在，可能是新建文件
  }

  // 计算新内容
  let newContent: string;
  try {
    newContent = await computeNewContent(edit, originalContent);
  } catch (error) {
    return {
      success: false,
      filePath: edit.filePath,
      error: error instanceof Error ? error.message : String(error),
    };
  }

  // 计算统计
  const stats = computeEditStats(originalContent, newContent, edit);

  // 试运行模式
  if (opts.dryRun) {
    return {
      success: true,
      filePath: edit.filePath,
      originalContent: originalContent ?? undefined,
      newContent,
      stats,
    };
  }

  // 创建备份
  if (opts.backup && originalContent !== null) {
    await createBackup(edit.filePath, originalContent, opts.backupDir);
  }

  // 确保目录存在
  try {
    await fs.mkdir(path.dirname(edit.filePath), { recursive: true });
  } catch (error) {
    return {
      success: false,
      filePath: edit.filePath,
      error: `创建目录失败: ${error instanceof Error ? error.message : String(error)}`,
    };
  }

  // 写入新内容
  try {
    await fs.writeFile(edit.filePath, newContent, 'utf-8');
  } catch (error) {
    return {
      success: false,
      filePath: edit.filePath,
      error: `写入文件失败: ${error instanceof Error ? error.message : String(error)}`,
    };
  }

  // 运行 LSP 诊断
  let diagnostics: string | undefined;
  if (opts.runDiagnostics && isLanguageSupported(edit.filePath)) {
    try {
      const isFirstStart = await touchFile(edit.filePath, originalContent === null);
      const waitTime = isFirstStart ? 2000 : 300;
      await new Promise(resolve => setTimeout(resolve, waitTime));
      diagnostics = await getFormattedFileDiagnostics(edit.filePath) ?? undefined;
    } catch {
      // LSP 错误不影响主流程
    }
  }

  return {
    success: true,
    filePath: edit.filePath,
    originalContent: originalContent ?? undefined,
    newContent,
    stats,
    diagnostics,
  };
}

/**
 * 计算新内容
 */
async function computeNewContent(edit: Edit, originalContent: string | null): Promise<string> {
  switch (edit.mode) {
    case 'whole':
      return computeWholeFileContent(edit);
    case 'search-replace':
      return computeSearchReplaceContent(edit, originalContent);
    case 'diff':
      return computeDiffContent(edit, originalContent);
    default:
      throw new Error(`不支持的编辑模式: ${(edit as Edit).mode}`);
  }
}

/**
 * 计算整文件替换的新内容
 */
function computeWholeFileContent(edit: WholeFileEdit): string {
  return edit.content;
}

/**
 * 计算搜索替换的新内容
 */
function computeSearchReplaceContent(
  edit: SearchReplaceEdit,
  originalContent: string | null
): string {
  if (originalContent === null) {
    throw new Error('搜索替换模式需要文件已存在');
  }

  let content = originalContent;

  for (const block of edit.blocks) {
    // 首先尝试直接匹配
    let positions = findSearchPositions(content, block.search);

    // 如果没有找到，尝试规范化后匹配
    if (positions.length === 0) {
      const normalizedSearch = normalizeSearchString(block.search, {
        trimTrailingWhitespace: true,
        normalizeLineEndings: true,
      });

      // 尝试在规范化的内容中查找
      const normalizedContent = normalizeSearchString(content, {
        trimTrailingWhitespace: true,
        normalizeLineEndings: true,
      });

      const normalizedPositions = findSearchPositions(normalizedContent, normalizedSearch);

      if (normalizedPositions.length === 1) {
        // 找到规范化匹配，需要在原始内容中找到对应位置
        // 使用逐行匹配的方式
        content = replaceWithNormalization(content, block.search, block.replace);
        continue;
      }

      if (normalizedPositions.length === 0) {
        throw new Error(`未找到要替换的内容`);
      }

      if (normalizedPositions.length > 1) {
        throw new Error(`找到 ${normalizedPositions.length} 处匹配，请提供更多上下文`);
      }
    }

    if (positions.length === 0) {
      throw new Error(`未找到要替换的内容`);
    }

    if (positions.length > 1) {
      throw new Error(`找到 ${positions.length} 处匹配，请提供更多上下文`);
    }

    // 执行替换
    content = content.replace(block.search, block.replace);
  }

  return content;
}

/**
 * 使用规范化方式替换内容
 */
function replaceWithNormalization(
  content: string,
  search: string,
  replace: string
): string {
  const searchLines = search.split('\n');
  const contentLines = content.split('\n');

  // 找到第一行的匹配位置
  const firstSearchLine = searchLines[0].trimEnd();

  for (let i = 0; i <= contentLines.length - searchLines.length; i++) {
    if (contentLines[i].trimEnd() === firstSearchLine) {
      // 检查后续行是否都匹配
      let allMatch = true;
      for (let j = 1; j < searchLines.length; j++) {
        if (contentLines[i + j].trimEnd() !== searchLines[j].trimEnd()) {
          allMatch = false;
          break;
        }
      }

      if (allMatch) {
        // 找到匹配，执行替换
        const replaceLines = replace.split('\n');
        const before = contentLines.slice(0, i);
        const after = contentLines.slice(i + searchLines.length);
        return [...before, ...replaceLines, ...after].join('\n');
      }
    }
  }

  throw new Error('规范化替换失败');
}

/**
 * 计算 diff 应用后的新内容
 */
function computeDiffContent(edit: DiffEdit, originalContent: string | null): string {
  if (originalContent === null) {
    // 新文件的 diff
    if (edit.patch.includes('new file mode')) {
      // 从 diff 中提取新增的内容
      const lines = edit.patch.split('\n');
      const newLines: string[] = [];

      for (const line of lines) {
        if (line.startsWith('+') && !line.startsWith('+++')) {
          newLines.push(line.slice(1));
        }
      }

      return newLines.join('\n');
    }

    throw new Error('Diff 模式需要文件已存在');
  }

  return applyDiffPatch(originalContent, edit.patch);
}

/**
 * 计算编辑统计
 */
function computeEditStats(
  originalContent: string | null,
  newContent: string,
  edit: Edit
): EditStats {
  const originalLines = originalContent?.split('\n') || [];
  const newLines = newContent.split('\n');

  // 简单统计：比较行数差异
  const additions = Math.max(0, newLines.length - originalLines.length);
  const deletions = Math.max(0, originalLines.length - newLines.length);

  // 更精确的统计
  let actualAdditions = 0;
  let actualDeletions = 0;

  if (originalContent !== null) {
    const originalSet = new Set(originalLines);
    const newSet = new Set(newLines);

    for (const line of newLines) {
      if (!originalSet.has(line)) {
        actualAdditions++;
      }
    }

    for (const line of originalLines) {
      if (!newSet.has(line)) {
        actualDeletions++;
      }
    }
  } else {
    actualAdditions = newLines.length;
  }

  const blocksApplied = edit.mode === 'search-replace' ? edit.blocks.length : 1;

  return {
    additions: actualAdditions || additions,
    deletions: actualDeletions || deletions,
    blocksApplied,
  };
}

/**
 * 创建备份
 */
async function createBackup(
  filePath: string,
  content: string,
  backupDir?: string
): Promise<string> {
  const dir = backupDir || path.join(path.dirname(filePath), '.edit-backups');
  await fs.mkdir(dir, { recursive: true });

  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
  const filename = path.basename(filePath);
  const backupPath = path.join(dir, `${filename}.${timestamp}.bak`);

  await fs.writeFile(backupPath, content, 'utf-8');
  return backupPath;
}

/**
 * 应用批量编辑
 */
export async function applyBatchEdits(
  batch: BatchEdit,
  options: ApplyEditOptions = {}
): Promise<BatchEditResult> {
  const results: EditApplyResult[] = [];
  const opts = { ...DEFAULT_OPTIONS, ...options };

  // 如果是原子操作，先验证所有编辑
  if (batch.atomic) {
    for (const edit of batch.edits) {
      const validation = await validateEdit(edit);
      if (!validation.valid) {
        return {
          success: false,
          results: [{
            success: false,
            filePath: edit.filePath,
            error: `验证失败: ${validation.errors.map(e => e.message).join('; ')}`,
          }],
          totalStats: { additions: 0, deletions: 0, blocksApplied: 0 },
        };
      }
    }
  }

  // 应用所有编辑
  const backups: Array<{ filePath: string; content: string }> = [];

  for (const edit of batch.edits) {
    // 对于原子操作，先保存原始内容用于回滚
    if (batch.atomic) {
      try {
        const content = await fs.readFile(edit.filePath, 'utf-8');
        backups.push({ filePath: edit.filePath, content });
      } catch {
        // 文件不存在，不需要备份
      }
    }

    const result = await applyEdit(edit, { ...opts, validate: !batch.atomic });
    results.push(result);

    // 原子操作下，如果有失败则回滚
    if (batch.atomic && !result.success) {
      // 回滚已完成的编辑
      for (const backup of backups) {
        try {
          await fs.writeFile(backup.filePath, backup.content, 'utf-8');
        } catch {
          // 回滚失败，记录但不抛出
          console.error(`回滚失败: ${backup.filePath}`);
        }
      }

      return {
        success: false,
        results,
        totalStats: { additions: 0, deletions: 0, blocksApplied: 0 },
      };
    }
  }

  // 计算总统计
  const totalStats: EditStats = {
    additions: results.reduce((sum, r) => sum + (r.stats?.additions || 0), 0),
    deletions: results.reduce((sum, r) => sum + (r.stats?.deletions || 0), 0),
    blocksApplied: results.reduce((sum, r) => sum + (r.stats?.blocksApplied || 0), 0),
  };

  return {
    success: results.every(r => r.success),
    results,
    totalStats,
  };
}

/**
 * 预览编辑效果
 */
export async function previewEdit(edit: Edit): Promise<EditApplyResult> {
  return applyEdit(edit, { dryRun: true, runDiagnostics: false });
}

/**
 * 预览批量编辑效果
 */
export async function previewBatchEdits(batch: BatchEdit): Promise<BatchEditResult> {
  return applyBatchEdits(batch, { dryRun: true, runDiagnostics: false });
}
