import { clampLimit, optionalIsoDate } from '@/services/shared/validate';
import { Op } from 'sequelize';
import db from '@/db/models';
import {
  STAFF_ATTENDANCE_DEFAULT_LIMIT,
  STAFF_ATTENDANCE_MAX_LIMIT,
  STAFF_ATTENDANCE_REPORT_ROLE_NAMES,
  STAFF_ATTENDANCE_STATUSES,
  STAFF_ATTENDANCE_TENANT_WIDE_ROLE_NAMES,
} from '@/shared/constants/staff-attendance';
import { STAFF_STATUSES } from '@/shared/constants/staff';
import {
  assertAuthenticatedTenantUser,
  campusScope,
  hasRoleAccess,
  requireOrganizationId,
  requireUserId,
} from '@/services/shared/access';
import type { StaffAttendanceRecords } from '@/db/models/staff_attendance_records';
import type { CurrentUser } from '@/db/api/types';

interface StaffAttendanceFilter {
  startDate?: unknown;
  endDate?: unknown;
  limit?: unknown;
}

/** Restricts records to the staff member, their campus, or the whole tenant. */
function visibilityScope(
  currentUser?: CurrentUser,
): { userId?: string; campusId?: string } {
  if (!hasRoleAccess(currentUser, STAFF_ATTENDANCE_REPORT_ROLE_NAMES)) {
    return { userId: requireUserId(currentUser) };
  }

  return campusScope(currentUser, STAFF_ATTENDANCE_TENANT_WIDE_ROLE_NAMES);
}

function dateFilter(filter: StaffAttendanceFilter): {
  attendance_date?: { [Op.gte]?: string; [Op.lte]?: string };
} {
  const startDate = optionalIsoDate(filter.startDate);
  const endDate = optionalIsoDate(filter.endDate);

  if (!startDate && !endDate) {
    return {};
  }

  return {
    attendance_date: {
      ...(startDate ? { [Op.gte]: startDate } : {}),
      ...(endDate ? { [Op.lte]: endDate } : {}),
    },
  };
}

function toRecordDto(record: StaffAttendanceRecords) {
  const plain = record.get({ plain: true });

  return {
    id: plain.id,
    date: plain.attendance_date,
    status: plain.status,
    note: plain.note,
    user_name: plain.user_name,
    user_role: plain.user_role,
    organizationId: plain.organizationId,
    campusId: plain.campusId,
    userId: plain.userId,
    createdAt: plain.createdAt,
    updatedAt: plain.updatedAt,
  };
}

class StaffAttendanceService {
  static async listRecords(
    filter: StaffAttendanceFilter,
    currentUser?: CurrentUser,
  ) {
    assertAuthenticatedTenantUser(currentUser);

    const result = await db.staff_attendance_records.findAndCountAll({
      where: {
        organizationId: requireOrganizationId(currentUser),
        ...visibilityScope(currentUser),
        ...dateFilter(filter),
      },
      limit: clampLimit(filter.limit, STAFF_ATTENDANCE_DEFAULT_LIMIT, STAFF_ATTENDANCE_MAX_LIMIT),
      order: [
        ['attendance_date', 'desc'],
        ['user_name', 'asc'],
      ],
    });

    return {
      rows: result.rows.map(toRecordDto),
      count: result.count,
    };
  }

  static async summary(
    filter: StaffAttendanceFilter,
    currentUser?: CurrentUser,
  ) {
    assertAuthenticatedTenantUser(currentUser);

    // Aggregate in SQL (COUNT per status) instead of fetching every record and
    // counting in JS — avoids the limit-truncated, incorrect totals and the
    // large row transfer.
    const recordsWhere = {
      organizationId: requireOrganizationId(currentUser),
      ...visibilityScope(currentUser),
      ...dateFilter(filter),
    };

    const [present, late, absent, staffCount] = await Promise.all([
      db.staff_attendance_records.count({
        where: { ...recordsWhere, status: STAFF_ATTENDANCE_STATUSES.PRESENT },
      }),
      db.staff_attendance_records.count({
        where: { ...recordsWhere, status: STAFF_ATTENDANCE_STATUSES.LATE },
      }),
      db.staff_attendance_records.count({
        where: { ...recordsWhere, status: STAFF_ATTENDANCE_STATUSES.ABSENT },
      }),
      db.staff.count({
        where: {
          organizationId: requireOrganizationId(currentUser),
          status: STAFF_STATUSES.ACTIVE,
          ...campusScope(currentUser, STAFF_ATTENDANCE_TENANT_WIDE_ROLE_NAMES),
        },
      }),
    ]);

    return {
      staffCount,
      recordsCount: present + late + absent,
      present,
      late,
      absent,
    };
  }
}

export default StaffAttendanceService;
