import fs from 'fs';
import path from 'path';
import type { Server } from 'socket.io';
import { isFaceVerifyEnabled } from '../config/faceVerify';
import {
  compareLiveSnapshotWithStudentPhoto,
} from './faceVerifyModels.service';
import {
  effectiveAlertThreshold,
  resolveFaceVerifyConfig,
} from './faceVerifyConfig.service';
import {
  emitFaceViolation,
  getOrCreateSession,
  incrementFailStreak,
  insertExamViolation,
  insertFaceVerifyLog,
  isInGracePeriod,
  markNotified,
  resetFailStreak,
  shouldSkipInterval,
  touchCompare,
} from './faceVerifySession.service';
import type {
  ExamFaceVerifyContext,
  FaceVerifyCompareResponse,
} from '../types/faceVerify.types';

function snapshotPathFromUrl(snapshotUrl: string): string {
  const cleaned = snapshotUrl.replace(/^\/+/, '');
  if (cleaned.includes('..')) {
    throw new Error('Invalid snapshot path');
  }
  return path.resolve(process.cwd(), cleaned);
}

export async function runFaceVerifyCompare(
  ctx: ExamFaceVerifyContext,
  io?: Server,
): Promise<FaceVerifyCompareResponse> {
  if (!isFaceVerifyEnabled()) {
    return {
      enabled: false,
      similarity: null,
      passed: true,
      failStreak: 0,
      notified: false,
      message: 'Face verify disabled',
      config: {
        preset: 'standard',
        similarity_alert_below: 65,
        similarity_critical_below: 45,
        fail_count_before_notify: 3,
        grace_period_start_min: 10,
      },
    };
  }

  const config = await resolveFaceVerifyConfig(
    ctx.courseId,
    ctx.recId,
    ctx.itemId,
  );

  if (!config.enabled) {
    return {
      enabled: false,
      similarity: null,
      passed: true,
      failStreak: 0,
      notified: false,
      message: 'Face verify disabled for this exam',
      config: {
        preset: config.preset,
        similarity_alert_below: config.similarity_alert_below,
        similarity_critical_below: config.similarity_critical_below,
        fail_count_before_notify: config.fail_count_before_notify,
        grace_period_start_min: config.grace_period_start_min,
      },
    };
  }

  const session = getOrCreateSession(ctx);
  if (shouldSkipInterval(ctx, config.check_interval_sec)) {
    return {
      enabled: true,
      similarity: null,
      passed: true,
      failStreak: session.failStreak,
      notified: false,
      message: 'ข้ามรอบ — ยังไม่ถึงช่วงเวลาตรวจครั้งถัดไป',
      config: {
        preset: config.preset,
        similarity_alert_below: config.similarity_alert_below,
        similarity_critical_below: config.similarity_critical_below,
        fail_count_before_notify: config.fail_count_before_notify,
        grace_period_start_min: config.grace_period_start_min,
      },
    };
  }

  touchCompare(ctx);

  const filePath = snapshotPathFromUrl(ctx.snapshotUrl);
  if (!fs.existsSync(filePath)) {
    throw new Error(`Snapshot file not found: ${ctx.snapshotUrl}`);
  }

  const liveBuffer = fs.readFileSync(filePath);
  const compare = await compareLiveSnapshotWithStudentPhoto(
    liveBuffer,
    ctx.userId,
  );

  const inGrace = isInGracePeriod(session, config.grace_period_start_min);
  const alertBelow = effectiveAlertThreshold(config, inGrace);

  let passed = true;
  let failStreak = session.failStreak;
  let notified = false;
  let message = compare.message;

  if (compare.similarity == null) {
    passed = true;
    message = compare.message;
  } else if (compare.similarity < alertBelow) {
    passed = false;
    failStreak = incrementFailStreak(ctx);
    message = `ใบหน้าไม่ตรงกับข้อมูล (ความคล้าย ${compare.similarity}%, เกณฑ์ ${alertBelow}%)`;

    if (failStreak >= config.fail_count_before_notify) {
      const reason = `พบการทุจริต: ใบหน้าไม่ตรงกับข้อมูลนักศึกษา (ความคล้าย ${compare.similarity}%)`;
      await insertExamViolation(ctx, reason);
      emitFaceViolation(io, ctx, reason);
      markNotified(ctx);
      notified = true;
    }
  } else {
    resetFailStreak(ctx);
    failStreak = 0;
    passed = true;
    message = `ผ่าน — ความคล้าย ${compare.similarity}%`;
  }

  await insertFaceVerifyLog({
    userId: ctx.userId,
    courseId: ctx.courseId,
    recId: ctx.recId,
    itemId: ctx.itemId,
    snapshotUrl: ctx.snapshotUrl,
    similarity: compare.similarity,
    passed,
    reason: message,
    failStreak,
    notified,
  });

  return {
    enabled: true,
    similarity: compare.similarity,
    passed,
    failStreak,
    notified,
    message,
    config: {
      preset: config.preset,
      similarity_alert_below: config.similarity_alert_below,
      similarity_critical_below: config.similarity_critical_below,
      fail_count_before_notify: config.fail_count_before_notify,
      grace_period_start_min: config.grace_period_start_min,
    },
  };
}

export async function warmUpFaceVerifyModels(): Promise<void> {
  if (!isFaceVerifyEnabled()) return;
  const { ensureFaceModelsLoaded } = await import('./faceVerifyModels.service');
  await ensureFaceModelsLoaded();
}
