import { db } from "@/lib/db"
import bcrypt from "bcryptjs"

import type {
  EmployeeListItem,
  EmployeeWithRelations,
  Department,
  SubDepartment,
  Branch,
  Shift,
} from "./types"
import type { EmployeeInput, EmployeeLeaveAllowanceInput } from "./schema"

import type {
  PrismaClient,
  Employee as PrismaEmployee,
  Department as PrismaDepartment,
  Prisma,
} from "@prisma/client"

const prisma = db as PrismaClient as unknown as any

async function getOrCreateEmployeeRoleId() {
  const existing = await prisma.role.findUnique({ where: { name: "EMPLOYEE" } })
  if (existing) return existing.id
  const role = await prisma.role.create({ data: { name: "EMPLOYEE" } })
  return role.id
}

export async function listEmployees(): Promise<EmployeeListItem[]> {
  const employees = await prisma.employee.findMany({
    orderBy: { createdAt: "desc" },
    include: {
      department: true,
      branch: true,
    },
  })

  return employees.map(
    (e: PrismaEmployee & { department?: PrismaDepartment | null; branch?: { name: string } | null }) => ({
      id: e.id,
      fullName: e.fullName,
      email: e.email ?? null,
      phone: e.phone ?? null,
      departmentName: e.department?.name ?? null,
      branchName: e.branch?.name ?? null,
      position: e.position ?? null,
      joiningDate: e.joiningDate ?? null,
    })
  )
}

// In service.ts — update getEmployeeById to include payrolls
export async function getEmployeeById(id: string): Promise<EmployeeWithRelations | null> {
  return prisma.employee.findUnique({
    where: { id },
    include: {
      department: true,
      subDepartment: true,
      branch: true,
      reportingManager: true,
      shifts: {
        include: { shift: true },
        orderBy: { createdAt: "desc" },
      },
      document: true,
      salaryComponents: {
        orderBy: { createdAt: "desc" },
      },
      leaveAllowance: true,
      leaveAllowances: {
        include: { leaveType: true },
        orderBy: { createdAt: "desc" },
      },
      payrolls: {
        orderBy: [{ year: "desc" }, { month: "desc" }],
      },
    },
  })
}

export async function listLeaveTypes() {
  return prisma.leaveType.findMany({ orderBy: { name: "asc" } })
}

async function resolveLeaveAllowances(input: EmployeeLeaveAllowanceInput[]) {
  const resolved: { leaveTypeId: string; days: number }[] = []

  for (const item of input) {
    const days = item.days ?? 0

    if (item.leaveTypeId) {
      resolved.push({ leaveTypeId: item.leaveTypeId, days })
      continue
    }

    const name = (item.leaveTypeName || "").trim()
    if (!name) continue

    const existing = await prisma.leaveType.findUnique({ where: { name } })
    const leaveType = existing
      ? existing
      : await prisma.leaveType.create({ data: { name } })

    resolved.push({ leaveTypeId: leaveType.id, days })
  }

  return resolved
}

export async function createEmployee(data: EmployeeInput) {
  let createdUserId: string | undefined
  if (data.createUser) {
    if (!data.userPassword) throw new Error("Password is required to create employee login")
    if (!data.email) throw new Error("Email is required to create employee login")

    const roleId = await getOrCreateEmployeeRoleId()
    const hashedPassword = await bcrypt.hash(data.userPassword, 12)

    const user = await prisma.user.create({
      data: {
        name: data.fullName,
        email: data.email,
        password: hashedPassword,
        isActive: true,
        roles: { create: [{ roleId }] },
      },
    })

    createdUserId = user.id
  }

  const employee = await prisma.employee.create({
    data: {
      fullName: data.fullName,
      dob: data.dob,
      joiningDate: data.joiningDate,
      email: data.email || undefined,
      phone: data.phone,
      civilId: data.civilId,
      nationality: data.nationality,
      address: data.address,
      position: data.position,
      description: data.description,
      userId: createdUserId,
      avatarUrl: data.avatarUrl,
      zktecoId: data.zktecoId || undefined,
      departmentId: data.departmentId || undefined,
      subDepartmentId: data.subDepartmentId || undefined,
      branchId: data.branchId || undefined,
      reportingManagerId: data.reportingManagerId || undefined,
      document: data.document
        ? {
          create: {
            civilIdExpiry: data.document.civilIdExpiry,
            passportNumber: data.document.passportNumber,
            passportExpiry: data.document.passportExpiry,
            civilIdFileUrl: data.document.civilIdFileUrl,
            civilIdFileName: data.document.civilIdFileName,
            passportFileUrl: data.document.passportFileUrl,
            passportFileName: data.document.passportFileName,
            otherFileUrl: data.document.otherFileUrl,
            otherFileName: data.document.otherFileName,
          },
        }
        : undefined,
      leaveAllowance: data.leaveAllowance
        ? {
          create: {
            annualDays: data.leaveAllowance.annualDays,
            sickDays: data.leaveAllowance.sickDays,
          },
        }
        : undefined,
      salaryComponents: data.salaryComponents?.length
        ? {
          createMany: {
            data: data.salaryComponents.map((s) => ({
              category: s.category,
              amount: s.amount,
            })),
          },
        }
        : undefined,
    } as Prisma.EmployeeUncheckedCreateInput,
  })

  if (data.shifts?.length) {
    await prisma.employeeShift.createMany({
      data: data.shifts.map((s) => ({
        employeeId: employee.id,
        shiftId: s.shiftId,
        effectiveFrom: s.effectiveFrom,
      })),
    })
  }

  if (data.leaveAllowances?.length) {
    const resolved = await resolveLeaveAllowances(data.leaveAllowances)
    if (resolved.length) {
      await prisma.employeeLeaveAllowance.createMany({
        data: resolved.map((r) => ({
          employeeId: employee.id,
          leaveTypeId: r.leaveTypeId,
          days: r.days,
        })),
      })
    }
  }

  return employee
}

export async function updateEmployee(id: string, data: EmployeeInput) {
  const existing = await prisma.employee.findUnique({
    where: { id },
    select: { userId: true },
  })

  if (data.createUser) {
    if (!existing?.userId) {
      if (!data.userPassword) throw new Error("Password is required to create employee login")
      if (!data.email) throw new Error("Email is required to create employee login")

      const roleId = await getOrCreateEmployeeRoleId()
      const hashedPassword = await bcrypt.hash(data.userPassword, 12)

      const user = await prisma.user.create({
        data: {
          name: data.fullName,
          email: data.email,
          password: hashedPassword,
          isActive: true,
          roles: { create: [{ roleId }] },
        },
      })

      await prisma.employee.update({ where: { id }, data: { userId: user.id } })
    } else {
      await prisma.user.update({
        where: { id: existing.userId },
        data: {
          name: data.fullName,
          email: data.email || undefined,
          ...(data.userPassword && data.userPassword.length > 0
            ? { password: await bcrypt.hash(data.userPassword, 12) }
            : {}),
        },
      })
    }
  }

  const employee = await prisma.employee.update({
    where: { id },
    data: {
      fullName: data.fullName,
      dob: data.dob,
      joiningDate: data.joiningDate,
      email: data.email || undefined,
      phone: data.phone,
      civilId: data.civilId,
      nationality: data.nationality,
      address: data.address,
      position: data.position,
      avatarUrl: data.avatarUrl,
      description: data.description,
      zktecoId: data.zktecoId || undefined,
      departmentId: data.departmentId || undefined,
      subDepartmentId: data.subDepartmentId || undefined,
      branchId: data.branchId || undefined,
      reportingManagerId: data.reportingManagerId || undefined,
    },
  })

  if (data.document) {
    const docData = {
      civilIdExpiry: data.document.civilIdExpiry,
      passportNumber: data.document.passportNumber,
      passportExpiry: data.document.passportExpiry,
      civilIdFileUrl: data.document.civilIdFileUrl,
      civilIdFileName: data.document.civilIdFileName,
      passportFileUrl: data.document.passportFileUrl,
      passportFileName: data.document.passportFileName,
      otherFileUrl: data.document.otherFileUrl,
      otherFileName: data.document.otherFileName,
    }

    await prisma.employeeDocument.upsert({
      where: { employeeId: id },
      create: { employeeId: id, ...docData },
      update: docData,
    })
  }

  if (data.leaveAllowance) {
    await prisma.leaveAllowance.upsert({
      where: { employeeId: id },
      create: { employeeId: id, annualDays: data.leaveAllowance.annualDays, sickDays: data.leaveAllowance.sickDays },
      update: { annualDays: data.leaveAllowance.annualDays, sickDays: data.leaveAllowance.sickDays },
    })
  }

  await prisma.salaryComponent.deleteMany({ where: { employeeId: id } })
  if (data.salaryComponents?.length) {
    await prisma.salaryComponent.createMany({
      data: data.salaryComponents.map((s) => ({
        employeeId: id,
        category: s.category,
        amount: s.amount,
      })),
    })
  }

  await prisma.employeeShift.deleteMany({ where: { employeeId: id } })
  if (data.shifts?.length) {
    await prisma.employeeShift.createMany({
      data: data.shifts.map((s) => ({
        employeeId: id,
        shiftId: s.shiftId,
        effectiveFrom: s.effectiveFrom,
      })),
    })
  }

  await prisma.employeeLeaveAllowance.deleteMany({ where: { employeeId: id } })
  if (data.leaveAllowances?.length) {
    const resolved = await resolveLeaveAllowances(data.leaveAllowances)
    if (resolved.length) {
      await prisma.employeeLeaveAllowance.createMany({
        data: resolved.map((r) => ({
          employeeId: id,
          leaveTypeId: r.leaveTypeId,
          days: r.days,
        })),
      })
    }
  }

  return employee
}

export async function deleteEmployee(id: string) {
  await prisma.employeeShift.deleteMany({ where: { employeeId: id } })
  await prisma.salaryComponent.deleteMany({ where: { employeeId: id } })
  await prisma.employeeDocument.deleteMany({ where: { employeeId: id } })
  await prisma.leaveAllowance.deleteMany({ where: { employeeId: id } })
  await prisma.employeeLeaveAllowance.deleteMany({ where: { employeeId: id } })
  return prisma.employee.delete({ where: { id } })
}

export async function listDepartments(): Promise<Department[]> {
  return prisma.department.findMany({ orderBy: { name: "asc" } })
}

export async function listSubDepartments(departmentId?: string): Promise<SubDepartment[]> {
  return prisma.subDepartment.findMany({
    where: departmentId ? { departmentId } : undefined,
    orderBy: { name: "asc" },
  })
}

export async function listBranches(): Promise<Branch[]> {
  return prisma.branch.findMany({ orderBy: { name: "asc" } })
}

export async function listShifts(): Promise<Shift[]> {
  return prisma.shift.findMany({ orderBy: { name: "asc" } })
}

export async function listReportingManagers(): Promise<{ id: string; fullName: string }[]> {
  return prisma.employee.findMany({
    orderBy: { fullName: "asc" },
    select: { id: true, fullName: true },
  })
}

const MANAGER_ROLE_NAMES = ["MANAGER", "GENERAL_MANAGER", "HR_MANAGER"]

export async function listManagers(): Promise<{ id: string; fullName: string }[]> {
  // Find all users who have a manager-level role
  const userRoles = await prisma.userRole.findMany({
    where: {
      role: {
        name: { in: MANAGER_ROLE_NAMES },
      },
    },
    select: {
      userId: true,
    },
  })

  const userIds = userRoles.map((ur: { userId: string }) => ur.userId)

  if (userIds.length === 0) return []

  // Get employees linked to those users
  const employees = await prisma.employee.findMany({
    where: {
      userId: { in: userIds },
    },
    orderBy: { fullName: "asc" },
    select: { id: true, fullName: true },
  })

  return employees
}