/**
 * Undo 管理器
 *
 * 参考 aider 的 undo 实现
 * 仅支持撤销 AI 生成的提交，且需要通过多项安全检查
 */

import type { GitRepo } from './repo.js';
import type { UndoConfig, UndoEntry, UndoResult, CommitInfo } from './types.js';

export class UndoManager {
  private repo: GitRepo;
  private config: UndoConfig;

  /** Undo 历史 */
  private history: UndoEntry[] = [];

  constructor(repo: GitRepo, config: UndoConfig) {
    this.repo = repo;
    this.config = config;
  }

  /**
   * 记录提交到 undo 历史
   */
  recordCommit(commitHash: string, shortHash: string, message: string, files: string[]): void {
    const entry: UndoEntry = {
      id: `undo-${Date.now()}`,
      timestamp: Date.now(),
      commitHash: shortHash,
      message,
      files,
      canUndo: true,
    };

    this.history.push(entry);

    // 限制历史记录数量
    if (this.history.length > this.config.maxHistory) {
      this.history = this.history.slice(-this.config.maxHistory);
    }
  }

  /**
   * 执行 undo 操作
   *
   * 安全检查清单（参考 aider）：
   * 1. 提交是否由 AI 生成
   * 2. 提交是否已推送到远程
   * 3. 是否为合并提交
   * 4. 文件是否有未提交的更改
   */
  async undo(): Promise<UndoResult> {
    // 1. 检查是否有可撤销的提交
    if (this.history.length === 0) {
      return {
        success: false,
        message: 'Nothing to undo. No AI commits in this session.',
      };
    }

    // 2. 获取最后一条记录
    const lastEntry = this.history[this.history.length - 1];

    // 3. 获取 HEAD 提交
    const headShortHash = await this.repo.getHeadShortHash();
    if (!headShortHash) {
      return {
        success: false,
        message: 'Unable to get HEAD commit.',
      };
    }

    // 4. 验证最后的提交是否与记录匹配
    if (headShortHash !== lastEntry.commitHash) {
      return {
        success: false,
        message: `The last commit (${headShortHash}) does not match the recorded AI commit (${lastEntry.commitHash}). The repository may have changed since the AI edit.`,
      };
    }

    // 5. 验证是否为 AI 生成的提交
    if (!this.repo.isAICommit(headShortHash)) {
      return {
        success: false,
        message: `The commit ${headShortHash} was not made by AI in this session.`,
      };
    }

    // 6. 检查提交是否已推送到远程
    const headHash = await this.repo.getHeadCommit();
    if (headHash) {
      const isPushed = await this.repo.isCommitPushed(headHash);
      if (isPushed) {
        return {
          success: false,
          message: 'The commit has already been pushed to remote. Undo is not safe.',
        };
      }
    }

    // 7. 检查文件是否有未提交的更改
    for (const file of lastEntry.files) {
      const isDirty = await this.repo.isDirty(file);
      if (isDirty) {
        return {
          success: false,
          message: `The file ${file} has uncommitted changes. Please commit or stash them first.`,
        };
      }
    }

    // 8. 执行撤销操作
    try {
      // 恢复文件到上一个版本
      const restoredFiles: string[] = [];
      for (const file of lastEntry.files) {
        try {
          await this.repo.checkoutFile('HEAD~1', file);
          restoredFiles.push(file);
        } catch {
          // 文件可能在前一个提交中不存在（新建的文件）
          // 这种情况下跳过
        }
      }

      // 软重置 HEAD
      await this.repo.resetSoft('HEAD~1');

      // 从历史中移除
      this.history.pop();

      // 从 AI 提交记录中移除
      this.repo.removeAICommitHash(lastEntry.commitHash);

      return {
        success: true,
        message: `Undone: ${lastEntry.commitHash} - ${lastEntry.message}`,
        commitHash: lastEntry.commitHash,
        restoredFiles,
      };
    } catch (error) {
      return {
        success: false,
        message: `Undo failed: ${error instanceof Error ? error.message : String(error)}`,
      };
    }
  }

  /**
   * 预览将要撤销的内容
   */
  getUndoPreview(): UndoEntry | null {
    if (this.history.length === 0) {
      return null;
    }
    return this.history[this.history.length - 1];
  }

  /**
   * 获取 undo 历史
   */
  getHistory(): UndoEntry[] {
    return [...this.history];
  }

  /**
   * 清除历史
   */
  clearHistory(): void {
    this.history = [];
  }

  /**
   * 检查是否可以 undo
   */
  async canUndo(): Promise<{ canUndo: boolean; reason?: string }> {
    if (this.history.length === 0) {
      return { canUndo: false, reason: 'No AI commits to undo' };
    }

    const lastEntry = this.history[this.history.length - 1];
    const headShortHash = await this.repo.getHeadShortHash();

    if (headShortHash !== lastEntry.commitHash) {
      return {
        canUndo: false,
        reason: 'Repository has changed since last AI commit',
      };
    }

    return { canUndo: true };
  }
}
