import type { RoutesManifest } from "../spec/schema";
import type { GuardViolation } from "./rules";
import { GUARD_RULES } from "./rules";
import { runGuardCheck } from "./check";
import { generateRoutes } from "../generator/generate";
import { beginChange, commitChange, rollbackChange } from "../change";


export interface AutoCorrectStep {
  ruleId: string;
  action: string;
  success: boolean;
  message: string;
}

export interface AutoCorrectResult {
  fixed: boolean;
  steps: AutoCorrectStep[];
  remainingViolations: GuardViolation[];
  retriedCount: number;
  /** 롤백이 발생했는지 여부 */
  rolledBack?: boolean;
  /** 관련 트랜잭션 ID */
  changeId?: string;
}

// 자동 수정 가능한 규칙들
const AUTO_CORRECTABLE_RULES = new Set([
  GUARD_RULES.GENERATED_MANUAL_EDIT.id,
  GUARD_RULES.SLOT_NOT_FOUND.id,
]);

export function isAutoCorrectableViolation(violation: GuardViolation): boolean {
  return AUTO_CORRECTABLE_RULES.has(violation.ruleId);
}

export async function runAutoCorrect(
  violations: GuardViolation[],
  manifest: RoutesManifest,
  rootDir: string,
  maxRetries: number = 3
): Promise<AutoCorrectResult> {
  const steps: AutoCorrectStep[] = [];
  let currentViolations = violations;
  let retriedCount = 0;

  // 1. 수정 전 스냅샷 생성 (트랜잭션 시작)
  let change;
  try {
    change = await beginChange(rootDir, {
      message: `Auto-correct: ${violations.length} violations`,
      autoGenerated: true,
    });
  } catch  {
    // 이미 활성 트랜잭션이 있는 경우 그대로 진행 (사용자가 수동으로 시작한 경우)
    // 다른 오류는 트랜잭션 없이 진행
  }

  try {
    while (retriedCount < maxRetries) {
      const autoCorrectableViolations = currentViolations.filter(isAutoCorrectableViolation);

      if (autoCorrectableViolations.length === 0) {
        break;
      }

      // 각 위반에 대해 수정 시도
      let anyFixed = false;

      for (const violation of autoCorrectableViolations) {
        const step = await correctViolation(violation, manifest, rootDir);
        steps.push(step);

        if (step.success) {
          anyFixed = true;
        }
      }

      if (!anyFixed) {
        // 아무것도 수정하지 못했으면 루프 종료
        break;
      }

      // Guard 재검사
      retriedCount++;
      const recheckResult = await runGuardCheck(manifest, rootDir);
      currentViolations = recheckResult.violations;

      if (recheckResult.passed) {
        // 2. 성공 시 커밋
        if (change) {
          await commitChange(rootDir, change.id);
        }
        return {
          fixed: true,
          steps,
          remainingViolations: [],
          retriedCount,
          changeId: change?.id,
        };
      }
    }

    // 3. 실패 시 (maxRetries 도달 또는 수정 불가)
    const fixed = currentViolations.length === 0;

    if (fixed && change) {
      // 모든 위반이 해결된 경우 커밋
      await commitChange(rootDir, change.id);
    } else if (!fixed && change) {
      // 실패 시 롤백
      await rollbackChange(rootDir, change.id);
      return {
        fixed: false,
        steps,
        remainingViolations: currentViolations,
        retriedCount,
        rolledBack: true,
        changeId: change.id,
      };
    }

    return {
      fixed,
      steps,
      remainingViolations: currentViolations,
      retriedCount,
      changeId: change?.id,
    };
  } catch (error) {
    // 4. 오류 발생 시 롤백
    if (change) {
      try {
        await rollbackChange(rootDir, change.id);
      } catch {
        // 롤백 실패는 무시 (원본 오류가 더 중요)
      }
    }
    throw error;
  }
}

async function correctViolation(
  violation: GuardViolation,
  manifest: RoutesManifest,
  rootDir: string
): Promise<AutoCorrectStep> {
  switch (violation.ruleId) {
    case GUARD_RULES.GENERATED_MANUAL_EDIT.id:
      return await correctGeneratedManualEdit(manifest, rootDir);

    case GUARD_RULES.SLOT_NOT_FOUND.id:
      return await correctSlotNotFound(manifest, rootDir);

    default:
      return {
        ruleId: violation.ruleId,
        action: "skip",
        success: false,
        message: `자동 수정 불가능한 규칙: ${violation.ruleId}`,
      };
  }
}

async function correctGeneratedManualEdit(
  manifest: RoutesManifest,
  rootDir: string
): Promise<AutoCorrectStep> {
  try {
    const result = await generateRoutes(manifest, rootDir);

    if (result.success) {
      return {
        ruleId: GUARD_RULES.GENERATED_MANUAL_EDIT.id,
        action: "generate",
        success: true,
        message: `코드 재생성 완료 (${result.created.length}개 파일)`,
      };
    } else {
      return {
        ruleId: GUARD_RULES.GENERATED_MANUAL_EDIT.id,
        action: "generate",
        success: false,
        message: `코드 재생성 실패: ${result.errors.join(", ")}`,
      };
    }
  } catch (error) {
    return {
      ruleId: GUARD_RULES.GENERATED_MANUAL_EDIT.id,
      action: "generate",
      success: false,
      message: `코드 재생성 실패: ${error instanceof Error ? error.message : String(error)}`,
    };
  }
}

async function correctSlotNotFound(
  manifest: RoutesManifest,
  rootDir: string
): Promise<AutoCorrectStep> {
  try {
    const result = await generateRoutes(manifest, rootDir);

    if (result.success) {
      const slotCount = result.created.filter((f) => f.includes("slots")).length;
      return {
        ruleId: GUARD_RULES.SLOT_NOT_FOUND.id,
        action: "generate-slot",
        success: true,
        message: `Slot 파일 생성 완료 (${slotCount}개 파일)`,
      };
    } else {
      return {
        ruleId: GUARD_RULES.SLOT_NOT_FOUND.id,
        action: "generate-slot",
        success: false,
        message: `Slot 파일 생성 실패: ${result.errors.join(", ")}`,
      };
    }
  } catch (error) {
    return {
      ruleId: GUARD_RULES.SLOT_NOT_FOUND.id,
      action: "generate-slot",
      success: false,
      message: `Slot 파일 생성 실패: ${error instanceof Error ? error.message : String(error)}`,
    };
  }
}
