import path from "path";
import type {
  ChangeRecord,
  TransactionState,
  BeginChangeOptions,
  CommitResult,
  RollbackResult,
} from "./types";
import { createSnapshot, writeSnapshot, readSnapshotById, restoreSnapshot } from "./snapshot";

const MANDU_DIR = ".mandu";
const HISTORY_DIR = "history";
const CHANGES_FILE = "changes.json";
const ACTIVE_FILE = "active.json";

/**
 * 변경 ID 생성 (YYYYMMDD-HHmmss-xxx)
 */
function generateChangeId(): string {
  const now = new Date();
  const date = now.toISOString().slice(0, 10).replace(/-/g, "");
  const time = now.toISOString().slice(11, 19).replace(/:/g, "");
  const random = Math.random().toString(36).slice(2, 5);
  return `${date}-${time}-${random}`;
}

/**
 * History 디렉토리 경로
 */
function getHistoryDir(rootDir: string): string {
  return path.join(rootDir, MANDU_DIR, HISTORY_DIR);
}

/**
 * Changes 파일 경로
 */
function getChangesPath(rootDir: string): string {
  return path.join(getHistoryDir(rootDir), CHANGES_FILE);
}

/**
 * Active 파일 경로
 */
function getActivePath(rootDir: string): string {
  return path.join(getHistoryDir(rootDir), ACTIVE_FILE);
}

/**
 * 모든 변경 기록 읽기
 */
async function readChanges(rootDir: string): Promise<ChangeRecord[]> {
  const changesPath = getChangesPath(rootDir);
  try {
    const file = Bun.file(changesPath);
    if (!(await file.exists())) {
      return [];
    }
    return await file.json();
  } catch {
    return [];
  }
}

/**
 * 변경 기록 저장
 */
async function writeChanges(rootDir: string, changes: ChangeRecord[]): Promise<void> {
  const changesPath = getChangesPath(rootDir);
  const historyDir = getHistoryDir(rootDir);

  // 디렉토리 확보
  await Bun.write(path.join(historyDir, ".gitkeep"), "");

  await Bun.write(changesPath, JSON.stringify(changes, null, 2));
}

/**
 * 현재 트랜잭션 상태 읽기
 */
async function readActiveState(rootDir: string): Promise<TransactionState> {
  const activePath = getActivePath(rootDir);
  try {
    const file = Bun.file(activePath);
    if (!(await file.exists())) {
      return { active: false, changeId: null, snapshotId: null };
    }
    return await file.json();
  } catch {
    return { active: false, changeId: null, snapshotId: null };
  }
}

/**
 * 트랜잭션 상태 저장
 */
async function writeActiveState(rootDir: string, state: TransactionState): Promise<void> {
  const activePath = getActivePath(rootDir);
  const historyDir = getHistoryDir(rootDir);

  // 디렉토리 확보
  await Bun.write(path.join(historyDir, ".gitkeep"), "");

  await Bun.write(activePath, JSON.stringify(state, null, 2));
}

/**
 * 활성 트랜잭션 존재 여부 확인
 */
export async function hasActiveTransaction(rootDir: string): Promise<boolean> {
  const state = await readActiveState(rootDir);
  return state.active;
}

/**
 * 현재 활성 트랜잭션 정보 조회
 */
export async function getActiveTransaction(rootDir: string): Promise<ChangeRecord | null> {
  const state = await readActiveState(rootDir);
  if (!state.active || !state.changeId) {
    return null;
  }

  const changes = await readChanges(rootDir);
  return changes.find((c) => c.id === state.changeId) || null;
}

/**
 * 변경 트랜잭션 시작
 * - 스냅샷 생성 후 활성 트랜잭션으로 등록
 */
export async function beginChange(
  rootDir: string,
  options: BeginChangeOptions = {}
): Promise<ChangeRecord> {
  // 이미 활성 트랜잭션이 있는지 확인
  const existingState = await readActiveState(rootDir);
  if (existingState.active) {
    const existingChange = await getActiveTransaction(rootDir);
    throw new Error(
      `이미 활성화된 트랜잭션이 있습니다: ${existingState.changeId}${existingChange?.message ? ` (${existingChange.message})` : ""}`
    );
  }

  // 스냅샷 생성
  const snapshot = await createSnapshot(rootDir);
  await writeSnapshot(rootDir, snapshot);

  // 변경 기록 생성
  const changeId = generateChangeId();
  const change: ChangeRecord = {
    id: changeId,
    message: options.message,
    status: "active",
    createdAt: new Date().toISOString(),
    snapshotId: snapshot.id,
    autoGenerated: options.autoGenerated,
  };

  // 변경 기록 추가
  const changes = await readChanges(rootDir);
  changes.push(change);
  await writeChanges(rootDir, changes);

  // 활성 상태 업데이트
  const activeState: TransactionState = {
    active: true,
    changeId: change.id,
    snapshotId: snapshot.id,
  };
  await writeActiveState(rootDir, activeState);

  return change;
}

/**
 * 변경 트랜잭션 커밋
 * - 현재 상태를 확정하고 트랜잭션 종료
 */
export async function commitChange(rootDir: string, changeId?: string): Promise<CommitResult> {
  const state = await readActiveState(rootDir);

  if (!state.active) {
    throw new Error("활성화된 트랜잭션이 없습니다");
  }

  const targetChangeId = changeId || state.changeId;
  if (!targetChangeId) {
    throw new Error("커밋할 변경 ID를 찾을 수 없습니다");
  }

  if (changeId && state.changeId !== changeId) {
    throw new Error(`지정된 변경(${changeId})이 현재 활성 트랜잭션(${state.changeId})과 다릅니다`);
  }

  // 변경 기록 업데이트
  const changes = await readChanges(rootDir);
  const changeIndex = changes.findIndex((c) => c.id === targetChangeId);

  if (changeIndex === -1) {
    throw new Error(`변경 기록을 찾을 수 없습니다: ${targetChangeId}`);
  }

  const change = changes[changeIndex];
  change.status = "committed";
  await writeChanges(rootDir, changes);

  // 활성 상태 초기화
  await writeActiveState(rootDir, {
    active: false,
    changeId: null,
    snapshotId: null,
  });

  return {
    success: true,
    changeId: targetChangeId,
    message: change.message,
  };
}

/**
 * 변경 트랜잭션 롤백
 * - 스냅샷으로 복원하고 트랜잭션 종료
 */
export async function rollbackChange(rootDir: string, changeId?: string): Promise<RollbackResult> {
  const state = await readActiveState(rootDir);

  // 활성 트랜잭션이 없으면 changeId로 찾기
  let targetChangeId: string;
  let targetSnapshotId: string;

  if (state.active && state.changeId) {
    targetChangeId = changeId || state.changeId;
    if (changeId && state.changeId !== changeId) {
      throw new Error(`지정된 변경(${changeId})이 현재 활성 트랜잭션(${state.changeId})과 다릅니다`);
    }
    targetSnapshotId = state.snapshotId!;
  } else if (changeId) {
    // 활성 트랜잭션이 없지만 changeId가 지정된 경우
    const changes = await readChanges(rootDir);
    const change = changes.find((c) => c.id === changeId);
    if (!change) {
      throw new Error(`변경 기록을 찾을 수 없습니다: ${changeId}`);
    }
    targetChangeId = changeId;
    targetSnapshotId = change.snapshotId;
  } else {
    throw new Error("롤백할 트랜잭션이 없습니다");
  }

  // 스냅샷 읽기
  const snapshot = await readSnapshotById(rootDir, targetSnapshotId);
  if (!snapshot) {
    throw new Error(`스냅샷을 찾을 수 없습니다: ${targetSnapshotId}`);
  }

  // 스냅샷으로 복원
  const restoreResult = await restoreSnapshot(rootDir, snapshot);

  // 변경 기록 업데이트
  const changes = await readChanges(rootDir);
  const changeIndex = changes.findIndex((c) => c.id === targetChangeId);

  if (changeIndex !== -1) {
    changes[changeIndex].status = "rolled_back";
    await writeChanges(rootDir, changes);
  }

  // 활성 상태 초기화
  await writeActiveState(rootDir, {
    active: false,
    changeId: null,
    snapshotId: null,
  });

  return {
    success: restoreResult.success,
    changeId: targetChangeId,
    restoreResult,
  };
}

/**
 * 트랜잭션 상태 조회
 */
export async function getTransactionStatus(
  rootDir: string
): Promise<{ state: TransactionState; change: ChangeRecord | null }> {
  const state = await readActiveState(rootDir);
  const change = state.active ? await getActiveTransaction(rootDir) : null;

  return { state, change };
}
