import { db } from "@/lib/db"
import type { PrismaClient } from "@prisma/client"
import type { AttendanceListItem, AttendanceFilters, AttendanceWithEmployee, AttendanceStatus } from "./types"
import type { AttendanceInput } from "./schema"

const prisma = db as PrismaClient as unknown as any

// ─── helpers ────────────────────────────────────────────────────────────────

/** normalise a YYYY-MM-DD string → start of day UTC DateTime */
function toStartOfDay(dateStr: string): Date {
  const d = new Date(dateStr)
  d.setUTCHours(0, 0, 0, 0)
  return d
}

/** combine a date + HH:mm string into a full DateTime */
function combineDateAndTime(dateStr: string, timeStr: string): Date {
  const [h, m] = timeStr.split(":").map(Number)
  const d = new Date(dateStr)
  d.setUTCHours(h, m, 0, 0)
  return d
}

// ─── queries ─────────────────────────────────────────────────────────────────

export async function listAttendance(filters: AttendanceFilters = {}): Promise<AttendanceListItem[]> {
  const where: Record<string, unknown> = {}

  if (filters.branchId)    where.branchId   = filters.branchId
  if (filters.employeeId)  where.employeeId = filters.employeeId
  if (filters.status)      where.status     = filters.status

  if (filters.dateFrom || filters.dateTo) {
    where.date = {
      ...(filters.dateFrom ? { gte: toStartOfDay(filters.dateFrom) } : {}),
      ...(filters.dateTo   ? { lte: toStartOfDay(filters.dateTo)   } : {}),
    }
  }

  const rows = await prisma.attendance.findMany({
    where,
    orderBy: [{ date: "desc" }, { createdAt: "desc" }],
    include: {
      employee: {
        select: {
          id: true,
          fullName: true,
          branch: { select: { id: true, name: true } },
        },
      },
    },
  })

  return rows.map((r: any) => ({
    id:           r.id,
    employeeId:   r.employeeId,
    employeeName: r.employee.fullName,
    branchName:   r.employee.branch?.name ?? null,
    date:         r.date,
    checkIn:      r.checkIn,
    checkOut:     r.checkOut,
    status:       r.status as AttendanceStatus,
    note:         r.note ?? null,
    recordedBy:   r.recordedBy ?? null,
  }))
}

export async function getAttendanceById(id: string): Promise<AttendanceWithEmployee | null> {
  return prisma.attendance.findUnique({
    where: { id },
    include: {
      employee: {
        select: {
          id: true,
          fullName: true,
          branch: { select: { id: true, name: true } },
        },
      },
    },
  })
}

/** Get today's attendance record for an employee (for self clock-in/out) */
export async function getTodayAttendance(employeeId: string) {
  const today = new Date()
  today.setUTCHours(0, 0, 0, 0)
  return prisma.attendance.findUnique({
    where: { employeeId_date: { employeeId, date: today } },
  })
}

// ─── mutations ───────────────────────────────────────────────────────────────

export async function createAttendance(data: AttendanceInput, recordedById?: string) {
  const date = toStartOfDay(data.date)

  return prisma.attendance.create({
    data: {
      employeeId: data.employeeId,
      branchId:   data.branchId  || undefined,
      date,
      checkIn:    data.checkIn   ? combineDateAndTime(data.date, data.checkIn)  : undefined,
      checkOut:   data.checkOut  ? combineDateAndTime(data.date, data.checkOut) : undefined,
      status:     data.status,
      note:       data.note      || undefined,
      recordedBy: recordedById   || undefined,
    },
  })
}

export async function updateAttendance(id: string, data: AttendanceInput, recordedById?: string) {
  const date = toStartOfDay(data.date)

  return prisma.attendance.update({
    where: { id },
    data: {
      employeeId: data.employeeId,
      branchId:   data.branchId  || undefined,
      date,
      checkIn:    data.checkIn   ? combineDateAndTime(data.date, data.checkIn)  : null,
      checkOut:   data.checkOut  ? combineDateAndTime(data.date, data.checkOut) : null,
      status:     data.status,
      note:       data.note      || undefined,
      recordedBy: recordedById   || undefined,
    },
  })
}

export async function deleteAttendance(id: string) {
  return prisma.attendance.delete({ where: { id } })
}

/** Employee self clock-in */
export async function clockIn(employeeId: string, branchId?: string) {
  const now   = new Date()
  const today = new Date(now)
  today.setUTCHours(0, 0, 0, 0)

  const existing = await prisma.attendance.findUnique({
    where: { employeeId_date: { employeeId, date: today } },
  })

  if (existing) {
    if (existing.checkIn) throw new Error("Already clocked in today")
    return prisma.attendance.update({
      where: { id: existing.id },
      data:  { checkIn: now, status: "present" },
    })
  }

  return prisma.attendance.create({
    data: {
      employeeId,
      branchId: branchId || undefined,
      date:     today,
      checkIn:  now,
      status:   "present",
    },
  })
}

/** Employee self clock-out */
export async function clockOut(employeeId: string) {
  const today = new Date()
  today.setUTCHours(0, 0, 0, 0)

  const existing = await prisma.attendance.findUnique({
    where: { employeeId_date: { employeeId, date: today } },
  })

  if (!existing)          throw new Error("No clock-in record found for today")
  if (!existing.checkIn)  throw new Error("Must clock in before clocking out")
  if (existing.checkOut)  throw new Error("Already clocked out today")

  return prisma.attendance.update({
    where: { id: existing.id },
    data:  { checkOut: new Date() },
  })
}

// ─── summary helpers (for dashboard) ────────────────────────────────────────

export async function getAttendanceSummary(branchId?: string, date?: string) {
  const targetDate = date ? toStartOfDay(date) : (() => { const d = new Date(); d.setUTCHours(0,0,0,0); return d })()
  const where: Record<string, unknown> = { date: targetDate }
  if (branchId) where.branchId = branchId

  const rows = await prisma.attendance.groupBy({
    by:    ["status"],
    where,
    _count: { status: true },
  })

  const summary: Record<string, number> = {
    present: 0, absent: 0, late: 0, half_day: 0, on_leave: 0,
  }
  for (const r of rows) {
    summary[r.status] = r._count.status
  }
  return summary
}