import { NestFactory } from "@nestjs/core"
import { DataSource, IsNull } from "typeorm"
import * as path from "path"
import * as fs from "fs"
import { AppModule } from "../app.module"
import { Employee } from "../modules/employees/entities/employee.entity"
import { LeaveType } from "../modules/leave-types/entities/leave-type.entity"
import {
  LeaveDayType,
  LeaveRequest,
  LeaveRequestStatus,
} from "../modules/leave-requests/entities/leave-request.entity"
import {
  ApprovalAction,
  LeaveRequestApprovalLog,
} from "../modules/leave-requests/entities/leave-request-approval-log.entity"

interface LeaveHistoryRecord {
  leaveRecord: {
    empleaveId: number
    dateFrom: string
    dateTo: string
    duration: string
    comments: string
    leaveStatus: string
    appliedBy: number
    leaveYear: string
    createdAt: string
    updatedAt: string
  }
  employee: {
    id: number
    name: string
    email: string
    phoneNo: string
    gender: string
    joiningDate: string
    roleId: string
    techId: number
  }
  leaveType: {
    leaveTypeId: number
    leaveName: string
    shortCode: string
  }
}

const HR_EMAIL = "hr@moweb.com"
const PAID_LEAVE_NAME = "Paid Leave"
const PAID_LEAVE_SUB_LEAVE_NAME = "Casual Leave"

const mapDuration = (
  duration: string,
): { durationDays: number; leaveDayType: string } => {
  if (duration === "f0.5") {
    return { durationDays: 0.5, leaveDayType: LeaveDayType.FIRST_HALF }
  }
  if (duration === "s0.5") {
    return { durationDays: 0.5, leaveDayType: LeaveDayType.SECOND_HALF }
  }
  return {
    durationDays: parseFloat(duration) || 1,
    leaveDayType: LeaveDayType.FULL,
  }
}

const mapStatus = (leaveStatus: string): LeaveRequestStatus => {
  if (leaveStatus === "Approved") return LeaveRequestStatus.APPROVED
  if (leaveStatus === "Cancelled") return LeaveRequestStatus.CANCELLED
  return LeaveRequestStatus.PENDING
}

const mapApprovalAction = (leaveStatus: string): ApprovalAction => {
  if (leaveStatus === "Cancelled")
    return ApprovalAction.CANCELLED_AFTER_APPROVAL
  return ApprovalAction.APPROVED
}

const calcFullDays = (dateFrom: string, dateTo: string): number => {
  const from = new Date(dateFrom)
  const to = new Date(dateTo)
  const diffMs = to.getTime() - from.getTime()
  return Math.round(diffMs / (1000 * 60 * 60 * 24)) + 1
}

const bootstrap = async () => {
  const app = await NestFactory.createApplicationContext(AppModule)
  const dataSource = app.get(DataSource)

  const jsonPath = path.join(__dirname, "leave-history.json")
  const rawData = fs.readFileSync(jsonPath, "utf-8")
  const records: LeaveHistoryRecord[] = JSON.parse(rawData)

  console.log(`\nTotal records to process: ${records.length}\n`)

  // Cache HR employee once
  const hrEmployee = await dataSource
    .getRepository(Employee)
    .findOne({ where: { email: HR_EMAIL.toLowerCase() } })

  if (!hrEmployee) {
    console.error(
      `ERROR: HR employee (${HR_EMAIL}) not found. Aborting import.`,
    )
    await app.close()
    process.exit(1)
  }

  // Caches to avoid redundant DB hits
  const employeeCache = new Map<string, Employee | null>()
  const leaveTypeCache = new Map<string, LeaveType | null>()
  const subLeaveCache = new Map<number, LeaveType | null>()

  let imported = 0
  let skipped = 0
  let failed = 0

  for (let i = 0; i < records.length; i++) {
    const record = records[i]
    const { leaveRecord, employee: empData, leaveType: leaveTypeData } = record

    try {
      // 1. Find employee by email
      const emailKey = empData.email.toLowerCase()
      if (!employeeCache.has(emailKey)) {
        const found = await dataSource
          .getRepository(Employee)
          .findOne({ where: { email: empData.email?.toLowerCase() } })
        employeeCache.set(emailKey, found ?? null)
      }
      const employee = employeeCache.get(emailKey)

      if (!employee) {
        console.warn(`[${i + 1}] SKIP – Employee not found: ${emailKey}`)
        skipped++
        continue
      }

      // 2. Find parent leave type by name (top-level: parent_id IS NULL)
      const ltKey = `${employee.company_id}::${leaveTypeData.leaveName}`
      if (!leaveTypeCache.has(ltKey)) {
        const found = await dataSource.getRepository(LeaveType).findOne({
          where: {
            name: leaveTypeData.leaveName,
            company_id: employee.company_id,
            parent_id: IsNull(),
          },
        })
        leaveTypeCache.set(ltKey, found ?? null)
      }
      const leaveType = leaveTypeCache.get(ltKey)

      if (!leaveType) {
        console.warn(
          `[${i + 1}] SKIP – Leave type not found: "${leaveTypeData.leaveName}" for company ${employee.company_id}`,
        )
        skipped++
        continue
      }

      // 3. Find sub-leave – only for "Paid Leave"; skip for all other types
      let subLeaveId: number | null = null
      if (leaveTypeData.leaveName === PAID_LEAVE_NAME) {
        if (!subLeaveCache.has(leaveType.id)) {
          const found = await dataSource.getRepository(LeaveType).findOne({
            where: {
              name: PAID_LEAVE_SUB_LEAVE_NAME,
              parent_id: leaveType.id,
              company_id: employee.company_id,
            },
          })
          subLeaveCache.set(leaveType.id, found ?? null)
        }
        const subLeave = subLeaveCache.get(leaveType.id)
        if (!subLeave) {
          console.warn(
            `[${i + 1}] SKIP – Sub-leave "${PAID_LEAVE_SUB_LEAVE_NAME}" not found under "${leaveTypeData.leaveName}" (id: ${leaveType.id})`,
          )
          skipped++
          continue
        }
        subLeaveId = subLeave.id
      }

      // 4. Map duration & leave day type
      let durationDays: number
      let leaveDayType: string

      if (leaveRecord.duration === "f0.5" || leaveRecord.duration === "s0.5") {
        const mapped = mapDuration(leaveRecord.duration)
        durationDays = mapped.durationDays
        leaveDayType = mapped.leaveDayType
      } else {
        durationDays = calcFullDays(leaveRecord.dateFrom, leaveRecord.dateTo)
        leaveDayType = LeaveDayType.FULL
      }

      // 5. Map status & approval fields
      const status = mapStatus(leaveRecord.leaveStatus)
      const isApproved = leaveRecord.leaveStatus === "Approved"
      const isCancelled = leaveRecord.leaveStatus === "Cancelled"
      const approvedById = isApproved ? hrEmployee.id : null
      const approvedAt = isApproved ? new Date(leaveRecord.updatedAt) : null

      // 6. Insert leave_request
      const leaveRequest = dataSource.getRepository(LeaveRequest).create({
        company_id: employee.company_id,
        employee_id: employee.id,
        leave_type_id: leaveType.id,
        sub_leave_type_id: subLeaveId,
        from_date: new Date(leaveRecord.dateFrom),
        to_date: new Date(leaveRecord.dateTo),
        duration_days: durationDays,
        reason: leaveRecord.comments || "",
        status,
        approved_by: approvedById,
        approved_at: approvedAt,
        comments: null,
        is_cancelled_after_approval: false,
        leave_day_type: leaveDayType,
        created_by: employee.id,
      })

      const savedLeaveRequest = await dataSource
        .getRepository(LeaveRequest)
        .save(leaveRequest)

      // 7. Insert leave_request_approval_logs (Approved & Cancelled)
      if (isApproved || isCancelled) {
        const log = dataSource.getRepository(LeaveRequestApprovalLog).create({
          leave_request_id: savedLeaveRequest.id,
          action: mapApprovalAction(leaveRecord.leaveStatus),
          approved_by: approvedById,
          rejected_by: null,
          reject_reason: null,
          cancel_reason: null,
          company_id: employee.company_id,
          created_by: hrEmployee.id,
        })
        await dataSource.getRepository(LeaveRequestApprovalLog).save(log)
      }

      imported++

      if (imported % 50 === 0) {
        console.log(
          `  Progress: ${imported} imported, ${skipped} skipped so far...`,
        )
      }
    } catch (err) {
      console.error(
        `[${i + 1}] ERROR processing record (empleaveId: ${leaveRecord.empleaveId}):`,
        err.message,
      )
      failed++
    }
  }

  console.log(`\n========================================`)
  console.log(`Import complete.`)
  console.log(`  Imported : ${imported}`)
  console.log(`  Skipped  : ${skipped}`)
  console.log(`  Failed   : ${failed}`)
  console.log(`========================================\n`)

  await app.close()
}

bootstrap().catch((error) => {
  console.error("Failed to import leave history:", error)
  process.exit(1)
})
