/**
 * 撤销/重做管理器 — 纯逻辑，操作不可变 PathEntry[]
 */

import type { PathEntry } from './path-entry';

export const OperationType = {
  ADD: 0,
  DELETE: 1,
  EDIT: 2,
  MOVE_UP: 3,
  MOVE_DOWN: 4,
  CLEAN: 5,
  CLEAR: 6,
  IMPORT: 7,
  TOGGLE: 8,
  IMPORT_BOTH: 9,
} as const;
export type OperationType = (typeof OperationType)[keyof typeof OperationType];

export const TargetType = { SYSTEM: 0, USER: 1 } as const;
export type TargetType = (typeof TargetType)[keyof typeof TargetType];

export interface OpRecord {
  type: OperationType;
  target: TargetType;
  index: number;
  count: number;
  oldPaths: PathEntry[];
  newPaths: PathEntry[];
  /** DELETE 操作专用：被删除的各路径的原始 index（升序） */
  indices?: number[];
  /** IMPORT_BOTH 专用：用户 hive 的旧路径 */
  oldPathsOther?: PathEntry[];
  /** IMPORT_BOTH 专用：用户 hive 的新路径 */
  newPathsOther?: PathEntry[];
}

const DEFAULT_MAX_SIZE = 50;

export class UndoRedoManager {
  private records: OpRecord[] = [];
  private current: number = -1;
  private readonly maxSize: number;

  constructor(maxSize: number = DEFAULT_MAX_SIZE) {
    this.maxSize = maxSize;
  }

  push(record: OpRecord): void {
    this.records = this.records.slice(0, this.current + 1);
    if (this.records.length >= this.maxSize) {
      this.records.shift();
    }
    this.records.push(record);
    this.current = this.records.length - 1;
  }

  undo(
    sysPaths: readonly PathEntry[],
    userPaths: readonly PathEntry[],
  ): [PathEntry[], PathEntry[]] | null {
    if (this.current < 0) return null;

    const rec = this.records[this.current];
    this.current--;

    const sys = [...sysPaths];
    const user = [...userPaths];
    const target = rec.target === TargetType.SYSTEM ? sys : user;

    switch (rec.type) {
      case OperationType.ADD:
        target.splice(target.length - rec.count, rec.count);
        break;
      case OperationType.DELETE:
        if (rec.indices) {
          for (let i = 0; i < rec.indices.length; i++) {
            target.splice(rec.indices[i], 0, rec.oldPaths[i]);
          }
        } else {
          for (let i = 0; i < rec.count; i++) {
            target.splice(rec.index + i, 0, rec.oldPaths[i]);
          }
        }
        break;
      case OperationType.EDIT:
        target[rec.index] = rec.oldPaths[0];
        break;
      case OperationType.MOVE_UP:
        [target[rec.index - 1], target[rec.index]] = [target[rec.index], target[rec.index - 1]];
        break;
      case OperationType.MOVE_DOWN:
        [target[rec.index], target[rec.index + 1]] = [target[rec.index + 1], target[rec.index]];
        break;
      case OperationType.CLEAN:
      case OperationType.IMPORT:
        target.length = 0;
        target.push(...rec.oldPaths);
        break;
      case OperationType.CLEAR:
        target.push(...rec.oldPaths);
        break;
      case OperationType.TOGGLE:
        target[rec.index] = rec.oldPaths[0];
        break;
      case OperationType.IMPORT_BOTH:
        sys.length = 0;
        sys.push(...rec.oldPaths);
        user.length = 0;
        user.push(...(rec.oldPathsOther || []));
        return [sys, user];
    }

    return [sys, user];
  }

  redo(
    sysPaths: readonly PathEntry[],
    userPaths: readonly PathEntry[],
  ): [PathEntry[], PathEntry[]] | null {
    if (this.current >= this.records.length - 1) return null;

    this.current++;
    const rec = this.records[this.current];

    const sys = [...sysPaths];
    const user = [...userPaths];
    const target = rec.target === TargetType.SYSTEM ? sys : user;

    switch (rec.type) {
      case OperationType.ADD:
        target.push(...rec.newPaths);
        break;
      case OperationType.DELETE:
        if (rec.indices) {
          for (let i = rec.indices.length - 1; i >= 0; i--) {
            target.splice(rec.indices[i], 1);
          }
        } else {
          for (let i = rec.count - 1; i >= 0; i--) {
            target.splice(rec.index + i, 1);
          }
        }
        break;
      case OperationType.EDIT:
        target[rec.index] = rec.newPaths[0];
        break;
      case OperationType.MOVE_UP:
        [target[rec.index - 1], target[rec.index]] = [target[rec.index], target[rec.index - 1]];
        break;
      case OperationType.MOVE_DOWN:
        [target[rec.index], target[rec.index + 1]] = [target[rec.index + 1], target[rec.index]];
        break;
      case OperationType.CLEAN:
      case OperationType.IMPORT:
        target.length = 0;
        target.push(...rec.newPaths);
        break;
      case OperationType.CLEAR:
        target.length = 0;
        break;
      case OperationType.TOGGLE:
        target[rec.index] = rec.newPaths[0];
        break;
      case OperationType.IMPORT_BOTH:
        sys.length = 0;
        sys.push(...rec.newPaths);
        user.length = 0;
        user.push(...(rec.newPathsOther || []));
        return [sys, user];
    }

    return [sys, user];
  }

  canUndo(): boolean {
    return this.current >= 0;
  }
  canRedo(): boolean {
    return this.current < this.records.length - 1;
  }
  clear(): void {
    this.records = [];
    this.current = -1;
  }
  get historyLength(): number {
    return this.records.length;
  }
}
