import type { FastifyInstance } from 'fastify';
import { eq, and, sql } from 'drizzle-orm';
import { attendanceLogs, attendanceRecords, students } from '../../db/schema/index.js';
import { calculateAttendanceSummary, determinePunchType } from '@org/utils';
import type { RawScan } from '@org/utils';

export interface ProcessScanInput {
  tenantId: string;
  studentId: string;
  deviceId: string;
  scanTime: Date;
}

/**
 * Core attendance engine. Called by both ADMS push and ZKLib poller.
 * 1. Inserts a new attendance_logs row with the next scan_sequence.
 * 2. Recalculates attendance_records summary for the student/date.
 * 3. Emits Socket.IO event to the tenant room.
 */
export async function processRawScan(fastify: FastifyInstance, input: ProcessScanInput) {
  const { tenantId, studentId, deviceId, scanTime } = input;
  const attendanceDate = toDateString(scanTime);

  // Get current scan count for this student today to compute next sequence
  const countResult = await fastify.db
    .select({ count: sql<number>`count(*)::int` })
    .from(attendanceLogs)
    .where(
      and(
        eq(attendanceLogs.tenantId, tenantId),
        eq(attendanceLogs.studentId, studentId),
        eq(attendanceLogs.attendanceDate, attendanceDate)
      )
    );

  const currentCount = countResult[0]?.count ?? 0;
  const scanSequence = currentCount + 1;
  const punchType = determinePunchType(scanSequence);

  // Insert the log entry
  const [log] = await fastify.db
    .insert(attendanceLogs)
    .values({ tenantId, studentId, deviceId, scanTime, attendanceDate, scanSequence, punchType })
    .returning();

  // Fetch all scans for the day to recalculate summary
  const allScans = await fastify.db
    .select({ scanTime: attendanceLogs.scanTime, scanSequence: attendanceLogs.scanSequence, punchType: attendanceLogs.punchType })
    .from(attendanceLogs)
    .where(
      and(
        eq(attendanceLogs.tenantId, tenantId),
        eq(attendanceLogs.studentId, studentId),
        eq(attendanceLogs.attendanceDate, attendanceDate)
      )
    )
    .orderBy(attendanceLogs.scanSequence);

  const rawScans: RawScan[] = allScans.map((s) => ({
    scanTime: new Date(s.scanTime),
    scanSequence: s.scanSequence,
    punchType: s.punchType as 'IN' | 'OUT',
  }));

  const summary = calculateAttendanceSummary(rawScans, fastify.config.LATE_THRESHOLD_MINUTES);

  // Upsert attendance_records
  const [record] = await fastify.db
    .insert(attendanceRecords)
    .values({
      tenantId,
      studentId,
      attendanceDate,
      totalPunches: rawScans.length,
      firstIn: summary.firstIn,
      lastOut: summary.lastOut,
      totalPresentMinutes: summary.totalPresentMinutes,
      totalBreakMinutes: summary.totalBreakMinutes,
      isCurrentlyInside: summary.isCurrentlyInside,
      status: summary.status,
      updatedAt: new Date(),
    })
    .onConflictDoUpdate({
      target: [attendanceRecords.tenantId, attendanceRecords.studentId, attendanceRecords.attendanceDate],
      set: {
        totalPunches: rawScans.length,
        firstIn: summary.firstIn,
        lastOut: summary.lastOut,
        totalPresentMinutes: summary.totalPresentMinutes,
        totalBreakMinutes: summary.totalBreakMinutes,
        isCurrentlyInside: summary.isCurrentlyInside,
        status: summary.status,
        updatedAt: new Date(),
      },
    })
    .returning();

  // Look up student name for socket payload
  const [student] = await fastify.db
    .select({ studentName: students.studentName })
    .from(students)
    .where(eq(students.id, studentId))
    .limit(1);

  // Emit real-time event to tenant room
  fastify.io.to(`tenant:${tenantId}`).emit('attendance:new', {
    log,
    record,
    studentId,
    studentName: student?.studentName ?? 'Unknown',
    punchType,
    scanTime: scanTime.toISOString(),
  });

  return { log, record };
}

function toDateString(date: Date): string {
  return date.toISOString().split('T')[0];
}
