import type { Server } from 'socket.io';
import { getLearningPool } from '../config/faceVerify';
import type { ExamFaceVerifyContext } from '../types/faceVerify.types';

type SessionKey = string;

interface FailSession {
  failStreak: number;
  examStartedAt: number;
  lastCompareAt: number;
  lastNotifyAt: number;
}

const sessions = new Map<SessionKey, FailSession>();

function sessionKey(ctx: ExamFaceVerifyContext): SessionKey {
  return `${ctx.userId}:${ctx.courseId}:${ctx.recId}:${ctx.itemId}`;
}

export function getOrCreateSession(ctx: ExamFaceVerifyContext): FailSession {
  const key = sessionKey(ctx);
  let s = sessions.get(key);
  if (!s) {
    s = {
      failStreak: 0,
      examStartedAt: Date.now(),
      lastCompareAt: 0,
      lastNotifyAt: 0,
    };
    sessions.set(key, s);
  }
  return s;
}

export function isInGracePeriod(
  session: FailSession,
  graceMinutes: number,
): boolean {
  const elapsedMs = Date.now() - session.examStartedAt;
  return elapsedMs < graceMinutes * 60_000;
}

export async function insertFaceVerifyLog(input: {
  userId: string;
  courseId: string;
  recId: string;
  itemId: string;
  snapshotUrl: string;
  similarity: number | null;
  passed: boolean;
  reason: string;
  failStreak: number;
  notified: boolean;
}): Promise<void> {
  const pool = getLearningPool();
  await pool.query(
    `INSERT INTO tb_face_verify_log
      (user_id, course_id, recid, itemid, snapshot_url, similarity, passed, reason, fail_streak, notified)
     VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
    [
      input.userId,
      input.courseId,
      input.recId,
      input.itemId,
      input.snapshotUrl,
      input.similarity,
      input.passed ? 1 : 0,
      input.reason,
      input.failStreak,
      input.notified ? 1 : 0,
    ],
  );
}

export async function insertExamViolation(ctx: ExamFaceVerifyContext, reason: string): Promise<void> {
  const pool = getLearningPool();
  await pool.query(
    `INSERT INTO tb_exam_violation
      (user_id, user_name, course_id, recid, itemid, reason, device_info, reported_at, status)
     VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), 'pending')`,
    [
      ctx.userId,
      ctx.userName,
      ctx.courseId,
      ctx.recId,
      ctx.itemId,
      reason,
      ctx.device || '',
    ],
  );
}

export function emitFaceViolation(
  io: Server | undefined,
  ctx: ExamFaceVerifyContext,
  reason: string,
): void {
  if (!io) return;
  io.to('exam:monitoring').emit('exam:violation:detected', {
    userId: ctx.userId,
    name: ctx.userName,
    courseId: ctx.courseId,
    recId: ctx.recId,
    itemId: ctx.itemId,
    reason,
    detectedAt: new Date().toISOString(),
    device: ctx.device,
  });
}

export function resetFailStreak(ctx: ExamFaceVerifyContext): void {
  const s = sessions.get(sessionKey(ctx));
  if (s) s.failStreak = 0;
}

export function incrementFailStreak(ctx: ExamFaceVerifyContext): number {
  const s = getOrCreateSession(ctx);
  s.failStreak += 1;
  s.lastCompareAt = Date.now();
  return s.failStreak;
}

export function markNotified(ctx: ExamFaceVerifyContext): void {
  const s = getOrCreateSession(ctx);
  s.lastNotifyAt = Date.now();
}

export function shouldSkipInterval(
  ctx: ExamFaceVerifyContext,
  intervalSec: number,
): boolean {
  const s = getOrCreateSession(ctx);
  if (!s.lastCompareAt) return false;
  return Date.now() - s.lastCompareAt < intervalSec * 1000;
}

export function touchCompare(ctx: ExamFaceVerifyContext): void {
  const s = getOrCreateSession(ctx);
  s.lastCompareAt = Date.now();
}
