import { Injectable, Logger } from "@nestjs/common"
import { Cron, CronExpression } from "@nestjs/schedule"
import { InjectRepository } from "@nestjs/typeorm"
import axios from "axios" // Added axios
import moment from "moment-timezone"
import { formatMessage } from "src/constants/notification-message"
import { STATUS } from "src/constants/trip.constant"
import { ILike, In, IsNull, LessThanOrEqual, Not, Repository } from "typeorm"
import { AuthService } from "../../auth/v1/auth.service"
import { ClientCompanyContract } from "../../client-contract/entities/client-contract.entity"
import { DriverAvailability } from "../../driver-availability/entities/driver-availability.entity"
import { NotificationService } from "../../notification/v1/notification.service"

import { TeamMember } from "../../team-member/entities/team_member.entity"
import { Trip } from "../../trips/entities/trip.entity"
import { Arrival, Departure, FlightData } from "../flight-data"
import { INVOICE_STATUS } from "src/constants/invoice.constant"
import { convertUtcToTimezone } from "src/utils/helpers"
import { VehicleMaintenance } from "../../vehicle-maintenance/entities/vehicle-maintenance.entity"
import { FleetManagement } from "../../fleet-management/entities/fleet-management.entity"
import { DriverFleetHistory } from "../../fleet-management/entities/driver-fleet-history.entity"
import { VehicleStatus } from "../../vehicle-status/entities/vehicle-status.entity"
import { ApiLog } from "../../api-log/entities/api-log.entity"
import { Auth } from "src/modules/auth/entities/auth.entity"
import { Role } from "src/modules/role/entities/role.entity"
import { Invoice } from "src/modules/invoices/entities/invoice.entity"

@Injectable()
export class CronService {
  private readonly logger = new Logger(CronService.name)

  constructor(
    @InjectRepository(Trip)
    private readonly tripRepository: Repository<Trip>,
    @InjectRepository(ClientCompanyContract)
    private readonly clientContactEntityRepository: Repository<ClientCompanyContract>,
    @InjectRepository(DriverAvailability)
    private readonly driverAvailabilityRepository: Repository<DriverAvailability>,
    @InjectRepository(Invoice)
    private readonly invoiceRepository: Repository<Invoice>,
    @InjectRepository(VehicleMaintenance)
    private readonly vehicleMaintenanceRepository: Repository<VehicleMaintenance>,
    @InjectRepository(FleetManagement)
    private readonly fleetRepository: Repository<FleetManagement>,
    @InjectRepository(DriverFleetHistory)
    private readonly driverFleetHistoryRepository: Repository<DriverFleetHistory>,
    @InjectRepository(VehicleStatus)
    private readonly vehicleStatusRepository: Repository<VehicleStatus>,
    @InjectRepository(ApiLog)
    private readonly apiLogRepository: Repository<ApiLog>,
    @InjectRepository(Auth)
    private readonly authRepository: Repository<Auth>,
    @InjectRepository(Role)
    private readonly roleRepository: Repository<Role>,
    @InjectRepository(TeamMember)
    private readonly teamMemberRepository: Repository<TeamMember>,
    private readonly notificationService: NotificationService,
    private readonly authService: AuthService,
    // private readonly roleRepository: RoleRepository,
  ) {}

  @Cron(CronExpression.EVERY_MINUTE) // Check every minute
  async everyMinuteCron() {
    this.handleTripReminderCron()
    this.handleTripOverdueCron()
    this.sendFlightReminder()
  }

  @Cron(CronExpression.EVERY_DAY_AT_MIDNIGHT) // Every day at midnight
  async everyMidnightCron() {
    this.handleContractsCron()
    this.handleInvoiceOverdueCron()
    this.updateMaintenances()
    this.removeOldApiLogs()
  }

  @Cron(CronExpression.EVERY_DAY_AT_1PM)
  async everyDayAt13UTC() {
    this.handleContractsReminderCron()
  }

  @Cron(CronExpression.EVERY_DAY_AT_9AM)
  async everyDayAt9UTC() {
    this.checkFleetRegistrationExpiry()
  }

  async sendFlightReminder() {
    const token = process.env.NEXT_PUBLIC_AVIATION_API_KEY
    if (!token) return
    this.logger.debug("Sending flight reminder...")

    const reminderTime = moment().utc().add(2, "hours")

    const trips = await this.tripRepository
      .createQueryBuilder("trip")
      .leftJoinAndSelect("trip.assignments", "assignments")
      .leftJoin("trip.trip_type", "trip_type")
      .leftJoinAndSelect("assignments.driver", "driver")
      .leftJoinAndSelect("driver.users", "driver_users")
      .where("trip.status IN (:...statuses)", {
        statuses: [STATUS.SCHEDULED],
      })
      .andWhere("trip.flight_number IS NOT NULL")
      .andWhere("trip.pickup_datetime = :datetime", {
        datetime: reminderTime.utc().toDate(),
      })
      .getMany()

    for (const trip of trips) {
      const flightByNumber = await this.searchFlightByNumber(
        trip.flight_number,
        1,
        10,
        token,
      )
      const driverUserIds =
        trip.assignments
          ?.map((a) => a.driver?.users?.map((u) => u.id))
          .flat()
          .filter(Boolean) ?? []
      if (flightByNumber.data.length > 0) {
        const flightData = flightByNumber.data[0]
        if (flightData.flight_status == "cancelled") {
          this.notificationService.sendNotification({
            user_ids: driverUserIds,
            title: `Flight ${trip.flight_number} update`,
            message: formatMessage("flightUpdateCancelled", {
              FLIGHT_NUMBER: trip.flight_number,
            }),
            data: {
              trip_id: trip.id,
              type: "trip",
            },
          })
        } else {
          let fData: Arrival | Departure
          if (trip.trip_type.name === "airport_pickup") {
            fData = flightData.arrival
          } else if (trip.trip_type.name === "airport_dropoff") {
            fData = flightData.departure
          }
          if (fData.delay) {
            this.notificationService.sendNotification({
              user_ids: driverUserIds,
              title: `Flight ${trip.flight_number} update`,
              message: formatMessage("flightUpdateDelayed", {
                FLIGHT_NUMBER: trip.flight_number,
                TIME: convertUtcToTimezone(
                  fData.scheduled,
                  trip?.trip_timezone,
                  "hh:mm A",
                ) as string,
                CUSTOMER: trip.customer?.customer_name ?? "N/A",
              }),
              data: {
                trip_id: trip.id,
                type: "trip",
              },
            })
          }
          if (!fData.delay) {
            this.notificationService.sendNotification({
              user_ids: driverUserIds,
              title: `Flight ${trip.flight_number} update`,
              message: formatMessage("flightUpdateOnSchedule", {
                FLIGHT_NUMBER: trip.flight_number,
                TIME: convertUtcToTimezone(
                  fData.scheduled,
                  trip?.trip_timezone,
                  "hh:mm A",
                ) as string,
                AIRPORT: fData.airport,
                CUSTOMER: trip.customer?.customer_name ?? "N/A",
              }),
              data: {
                trip_id: trip.id,
                type: "trip",
              },
            })
          }
        }
      }
    }
  }

  public async searchFlightByNumber(
    flightNumber: string,
    page: number = 1,
    limit: number = 10,
    token: any,
    flightDate?: string,
  ): Promise<FlightData> {
    try {
      const currentDate = flightDate || moment().utc().format("YYYY-MM-DD")
      const offset = (page - 1) * limit

      const API_BASE_URL = "https://api.aviationstack.com/v1"

      const response = await axios.get(`${API_BASE_URL}/flights`, {
        params: {
          access_key: token.toString(),
          flight_iata: flightNumber,
          flight_date: currentDate,
          limit,
          offset,
        },
      })
      return response.data
    } catch (error) {
      console.error("Error fetching flight data:", error)
      throw error
    }
  }

  async handleTripReminderCron() {
    this.logger.debug("Checking for upcoming trips to send reminders...")

    const trips = await this.tripRepository
      .createQueryBuilder("trip")
      .leftJoinAndSelect("trip.customer", "customer")
      .leftJoinAndSelect("trip.assignments", "assignments")
      .leftJoinAndSelect("assignments.driver", "driver")
      .leftJoinAndSelect("driver.users", "driver_users")
      .where("trip.status IN (:...statuses)", {
        statuses: [STATUS.SCHEDULED],
      })
      .getMany()

    const upcomingTrips = trips.filter((trip) => {
      if (!trip.trip_timezone || !trip.pickup_datetime) return false

      const nowInTripTz = moment().tz(trip.trip_timezone)
      const pickupInTripTz = moment(trip.pickup_datetime).tz(trip.trip_timezone)
      const reminderTime = pickupInTripTz.clone().subtract(60, "minutes")
      const reminderEnd = reminderTime.clone().add(1, "minutes")

      return (
        nowInTripTz.isAfter(reminderTime) &&
        nowInTripTz.isSameOrBefore(reminderEnd)
      )
    })

    for (const trip of upcomingTrips) {
      const driverUserIds =
        trip.assignments
          ?.map((a) => a.driver?.users?.map((u) => u.id))
          .flat()
          .filter(Boolean) ?? []

      if (driverUserIds.length > 0) {
        this.notificationService.sendNotification({
          user_ids: driverUserIds,
          title: "Trip Reminder",
          message: formatMessage("tripReminder", {
            CUSTOMER: trip.customer.customer_name,
            MINUTES: "60",
          }),
          data: {
            trip_id: trip.id,
            type: "trip",
          },
        })

        const driver = trip.assignments?.[0]?.driver
        let driverName = "Driver"

        if (driver) {
          driverName = driver.first_name + " " + driver.last_name
        }
        this.notificationService.sendCustomerNotification({
          user_ids: [trip.customer_id],
          title: "Trip Reminder",
          message: formatMessage("tripReminderCustomer", {
            PICKUP_LOCATION: trip.pick_up_location,
            TIME: convertUtcToTimezone(
              trip?.pickup_datetime,
              trip?.trip_timezone,
              "hh:mm A",
            ) as string,
            DRIVER_NAME: driverName ?? "N/A",
          }),
          data: {
            trip_id: trip.id,
            type: "trip",
          },
        })
        this.logger.log(
          `Sent trip reminder for Trip ID: ${trip.id} to drivers.`,
        )
      } else {
        this.logger.warn(
          `No drivers found for Trip ID: ${trip.id} to send reminder.`,
        )
      }
    }
  }

  async handleTripOverdueCron() {
    this.logger.debug("Checking for overdue trips to send reminders...")

    const trips = await this.tripRepository
      .createQueryBuilder("trip")
      .leftJoinAndSelect("trip.customer", "customer")
      .leftJoinAndSelect("trip.assignments", "assignments")
      .leftJoinAndSelect("assignments.driver", "driver")
      .leftJoinAndSelect("driver.users", "driver_users")
      .where("trip.status IN (:...statuses)", {
        statuses: [STATUS.SCHEDULED],
      })
      .getMany()

    const overdueTrips = trips.filter((trip) => {
      if (!trip.trip_timezone || !trip.pickup_datetime) return false

      const nowInTripTz = moment().tz(trip.trip_timezone)
      const pickupInTripTz = moment(trip.pickup_datetime).tz(trip.trip_timezone)
      const overdueTime = pickupInTripTz.clone().add(10, "minutes")
      const overdueEnd = overdueTime.clone().add(1, "minutes")

      return (
        nowInTripTz.isAfter(overdueTime) &&
        nowInTripTz.isSameOrBefore(overdueEnd)
      )
    })

    for (const trip of overdueTrips) {
      const driverUserIds =
        trip.assignments
          ?.map((a) => a.driver?.users?.map((u) => u.id))
          .flat()
          .filter(Boolean) ?? []

      if (driverUserIds.length > 0) {
        this.notificationService.sendNotification({
          user_ids: driverUserIds,
          title: "Trip Overdue",
          message: formatMessage("tripOverdue", {
            MINUTES: "10",
          }),
          data: {
            trip_id: trip.id,
            type: "trip",
          },
        })
        this.logger.log(
          `Sent trip overdue reminder for Trip ID: ${trip.id} to drivers.`,
        )
      } else {
        this.logger.warn(
          `No drivers found for overdue Trip ID: ${trip.id} to send reminder.`,
        )
      }
    }
  }

  async handleContractsCron() {
    this.logger.debug("Running contract expiry/activation cron...")

    const today = moment().startOf("day").toDate()

    // Fetch all contracts with company relation
    const allContracts = await this.clientContactEntityRepository.find({
      relations: ["authorized_contact", "authorized_contact.client_company"],
    })

    // Track companies with expired contracts
    const companiesWithExpiredContracts = new Set<number>()

    for (const contract of allContracts) {
      let updated = false
      const startDate = moment(contract.start_date).startOf("day")
      const endDate = moment(contract.end_date).endOf("day")
      const todayMoment = moment(today)

      // 🟠 CASE 1: Expired
      if (todayMoment.isAfter(endDate)) {
        if (contract.status !== "expired") {
          contract.status = "expired"
          await this.clientContactEntityRepository.save(contract)
          this.logger.log(`Contract ID ${contract.id} expired.`)
          updated = true

          companiesWithExpiredContracts.add(
            contract.authorized_contact.client_company.id,
          )
        }
      }

      // 🟢 CASE 2: Active (today between start & end, or exactly start date)
      else if (
        todayMoment.isSameOrAfter(startDate) &&
        todayMoment.isSameOrBefore(endDate)
      ) {
        if (contract.status !== "active") {
          contract.status = "active"
          await this.clientContactEntityRepository.save(contract)
          this.logger.log(`Contract ID ${contract.id} set to active.`)
          updated = true
        }
      }

      // 🔵 CASE 3: Upcoming (future start date)
      else if (todayMoment.isBefore(startDate)) {
        if (contract.status !== "upcoming") {
          contract.status = "upcoming"
          await this.clientContactEntityRepository.save(contract)
          this.logger.log(`Contract ID ${contract.id} set to upcoming.`)
          updated = true
        }
      }

      if (!updated) {
        this.logger.log(`No action required for Contract ID ${contract.id}.`)
      }
    }

    // 🟣 Activate next upcoming contract only for companies with expired contracts
    for (const companyId of companiesWithExpiredContracts) {
      const nearestUpcoming = await this.clientContactEntityRepository.findOne({
        where: {
          status: "upcoming",
          authorized_contact: {
            client_company: {
              id: companyId,
            },
          },
        },
        order: { start_date: "ASC" },
      })

      if (nearestUpcoming) {
        const startDate = moment(nearestUpcoming.start_date).startOf("day")
        const todayMoment = moment(today)

        // ✅ Only activate if start_date is today or in the past
        if (todayMoment.isSameOrAfter(startDate)) {
          nearestUpcoming.status = "active"
          await this.clientContactEntityRepository.save(nearestUpcoming)
          this.logger.log(
            `Contract ID ${nearestUpcoming.id} automatically promoted to active for company ${companyId} after contract expiry.`,
          )
        }
      }
    }

    this.logger.debug("Contract cron completed successfully.")
  }

  async handleContractsReminderCron() {
    this.logger.debug("Running contract reminder cron...")

    const today = moment.tz("America/New_York").startOf("day")
    const thirtyDaysFromToday = today
      .clone()
      .add(30, "days")
      .format("YYYY-MM-DD")
    const sevenDaysFromToday = today.clone().add(7, "days").format("YYYY-MM-DD")

    // Fetch contracts where end_date is exactly 30 days or 7 days from today
    const contractsToRemind = await this.clientContactEntityRepository
      .createQueryBuilder("contract")
      .leftJoinAndSelect("contract.authorized_contact", "authorized_contact")
      .leftJoinAndSelect("authorized_contact.client_company", "client_company")
      .where("contract.end_date = :thirtyDaysFromToday", {
        thirtyDaysFromToday,
      })
      .orWhere("contract.end_date = :sevenDaysFromToday", {
        sevenDaysFromToday,
      })
      .getMany()

    for (const contract of contractsToRemind) {
      const admins = await this.authService.getAdmins()
      const userIds = admins.map((admin) => admin.id)

      if (userIds.length > 0) {
        const clientName =
          contract.authorized_contact.client_company?.company_name ||
          "Unknown Client"

        this.notificationService.sendNotification({
          user_ids: userIds,
          title: `Contract Expiry Reminder`,
          message: formatMessage("clientContactRenewalReminder", {
            CLIENT: clientName,
            EXPIRY_DATE: contract.end_date.toDateString(),
          }),
          data: {
            type: "client_contact",
            contract_id: contract.id,
          },
        })
      } else {
        this.logger.warn(
          `No contact found for Contract ID: ${contract.id} to send reminder.`,
        )
      }
    }

    this.logger.debug("Contract reminder cron completed successfully.")
  }

  @Cron("0 0 * * 1") // Monday 12:00 AM (right after Sunday ends)
  async weeklyDriverAvailabilityReset() {
    this.logger.debug("Resetting driver availability for new week...")

    try {
      // 1. Find Driver role(s) dynamically (case-insensitive)
      // const driverRoles = await this.roleRepository
      //   .createQueryBuilder("role")
      //   .where("LOWER(role.name) = :driverName", { driverName: "driver" })
      //   .getMany()

      const driverRoles = (await this.roleRepository.findOne({
        where: { name: "Driver" },
      })) as any

      if (driverRoles.length === 0) {
        this.logger.warn("No 'Driver' role found, skipping reset")
        return
      }

      const driverRoleIds = driverRoles.map((r) => r.id)

      // 2. Get all team members who are drivers
      const activeDrivers = await this.teamMemberRepository
        .createQueryBuilder("team_member")
        .leftJoinAndSelect("team_member.users", "users")
        .andWhere("users.role_id IN (:...roleIds)", { roleIds: driverRoleIds })
        .andWhere("team_member.deleted_at IS NULL")
        .getMany()

      if (activeDrivers.length === 0) {
        this.logger.warn("No active drivers found for availability reset")
        return
      }

      // 3. Extract IDs
      const driverIds = activeDrivers.map((driver) => driver.id)
      const daysOfWeek = [
        "monday",
        "tuesday",
        "wednesday",
        "thursday",
        "friday",
        "saturday",
        "sunday",
      ]

      // 4. Delete old availability records
      const deleteResult = await this.driverAvailabilityRepository.delete({
        driver_id: In(driverIds),
      })

      this.logger.log(
        `Deleted ${deleteResult.affected || 0} old availability records`,
      )

      // 5. Insert new default availability records
      const newRecords = []
      for (const driver of activeDrivers) {
        for (const day of daysOfWeek) {
          newRecords.push({
            driver_id: driver.id,
            day_of_week: day,
            start_time: "09:00:00",
            end_time: "18:00:00",
            is_available: true,
          })
        }
      }

      await this.driverAvailabilityRepository.save(newRecords)

      this.logger.log(
        `Created ${newRecords.length} new availability records for ${activeDrivers.length} drivers`,
      )
    } catch (error) {
      this.logger.error("Error in weekly availability reset:", error)
    }
  }

  async handleInvoiceOverdueCron() {
    this.logger.debug("Checking for overdue invoices...")

    try {
      const today = moment().tz("America/New_York").startOf("day").toDate()

      // Fetch all invoices where due_date has passed and status is not paid
      const invoices = await this.invoiceRepository
        .createQueryBuilder("invoice")
        .where("invoice.due_date < :today", { today })
        .andWhere("invoice.status NOT IN (:...statuses)", {
          statuses: [INVOICE_STATUS.PAID],
        })
        .getMany()

      let updatedCount = 0

      for (const invoice of invoices) {
        const totalAmount = parseFloat(String(invoice.total_amount || "0"))
        const receivedAmount = parseFloat(
          String(invoice.payment_received_amount || "0"),
        )

        let newStatus = invoice.status

        // === CASE 1: No payment or unpaid/due invoices -> OVERDUE
        if (
          receivedAmount === 0 ||
          invoice.status === INVOICE_STATUS.UNPAID ||
          invoice.status === INVOICE_STATUS.DUE
        ) {
          newStatus = INVOICE_STATUS.OVERDUE
        }

        // === CASE 2: Partial payment and previously PARTIALLY_PAID or OVERDUE -> PARTIALLY_OVERDUE
        else if (
          receivedAmount > 0 &&
          receivedAmount < totalAmount &&
          [INVOICE_STATUS.PARTIALLY_PAID, INVOICE_STATUS.OVERDUE].includes(
            invoice.status,
          )
        ) {
          newStatus = INVOICE_STATUS.PARTIALLY_OVERDUE
        }

        // === CASE 3: Full payment received and previously OVERDUE or PARTIALLY_OVERDUE -> PAID
        else if (
          receivedAmount >= totalAmount &&
          [INVOICE_STATUS.OVERDUE, INVOICE_STATUS.PARTIALLY_OVERDUE].includes(
            invoice.status,
          )
        ) {
          newStatus = INVOICE_STATUS.PAID
        }

        // Update status if changed
        if (newStatus !== invoice.status) {
          invoice.status = newStatus
          await this.invoiceRepository.save(invoice)
          updatedCount++
          this.logger.log(`Invoice ID ${invoice.id} updated to ${newStatus}`)
        }
      }

      this.logger.log(
        `Invoice overdue cron completed. Updated ${updatedCount} invoices.`,
      )
    } catch (error) {
      this.logger.error("Error in invoice overdue cron:", error)
    }
  }

  async updateMaintenances() {
    this.logger.debug("Checking for fleets with scheduled maintenance today...")

    try {
      // Get current date start (00:00:00) using moment
      const today = moment().startOf("day").toDate()

      // Get 'Under Maintenance' status dynamically
      const underMaintenanceStatus = await this.vehicleStatusRepository.findOne(
        {
          where: { name: ILike("%Under Maintenance%") },
        },
      )

      // Find maintenances scheduled for today or before
      const scheduledMaintenances =
        await this.vehicleMaintenanceRepository.find({
          where: {
            next_scheduled_maintenance: LessThanOrEqual(today),
            status: Not("under_maintenance"),
          },
          relations: ["fleet"],
        })

      for (const maintenance of scheduledMaintenances) {
        if (!maintenance.fleet) continue

        const fleet = await this.fleetRepository.findOne({
          where: { id: maintenance.fleet.id },
        })

        if (!fleet) continue

        // Update fleet status to "Under Maintenance"
        fleet.vehicle_status_id = underMaintenanceStatus.id

        // If driver assigned → release and update history
        if (fleet.assigned_driver) {
          const driverId = fleet.assigned_driver

          const history = await this.driverFleetHistoryRepository.findOne({
            where: {
              fleet: { id: fleet.id },
              driver: { id: driverId },
              released_at: null,
            },
          })

          if (history) {
            history.released_at = moment().toDate()
            await this.driverFleetHistoryRepository.save(history)
          }

          fleet.assigned_driver = null

          this.logger.log(
            `Fleet ${fleet.id}: Driver ${driverId} released at ${moment().format(
              "YYYY-MM-DD HH:mm:ss",
            )}`,
          )
        }

        // Save fleet & maintenance updates
        await this.fleetRepository.save(fleet)
      }

      this.logger.log(
        `${scheduledMaintenances.length} fleets moved to 'Under Maintenance' and drivers released.`,
      )
    } catch (error) {
      this.logger.error("Error in updateMaintenances cron:", error)
    }
  }

  async removeOldApiLogs() {
    this.logger.debug("Removing old API logs...")

    try {
      const retentionDays = parseInt(
        process.env.API_LOG_RETENTION_DAYS || "30",
        10,
      )
      const cutoffDate = moment().subtract(retentionDays, "days").toDate()

      const deleteResult = await this.apiLogRepository.delete({
        created_at: LessThanOrEqual(cutoffDate.toISOString()),
      })

      this.logger.log(
        `Removed ${deleteResult.affected || 0} old API logs older than ${retentionDays} days.`,
      )
    } catch (error) {
      this.logger.error("Error in removeOldApiLogs cron:", error)
    }
  }

  async checkFleetRegistrationExpiry() {
    this.logger.debug("Checking for expiring fleet registrations...")

    try {
      // Calculate exactly 1 month from today (same date, previous month)
      const oneMonthFromToday = moment().add(1, "month").format("YYYY-MM-DD")

      // Find vehicles expiring exactly 1 month from today
      const expiringVehicles = await this.fleetRepository
        .createQueryBuilder("fleet")
        .leftJoinAndSelect("fleet.assigned_dispatcher", "dispatcher")
        .where("DATE(fleet.registration_expiry_date) = :oneMonthFromToday", {
          oneMonthFromToday,
        })
        .andWhere("fleet.deleted_at IS NULL")
        .select([
          "fleet.id",
          "fleet.car_code",
          "fleet.registration_number",
          "fleet.registration_expiry_date",
          "fleet.assigned_dispatcher_id",
          "fleet.vehicle_ownership",
          "dispatcher.id",
          "dispatcher.first_name",
          "dispatcher.last_name",
        ])
        .getMany()

      if (!expiringVehicles || expiringVehicles.length === 0) {
        this.logger.log("No vehicles found expiring exactly 1 month from today")
        return
      }

      this.logger.log(
        `Found ${expiringVehicles.length} vehicle(s) expiring in exactly 1 month`,
      )

      // Get Admin and Super Admin role IDs
      const adminRoles = await this.roleRepository.find({
        where: {
          name: In(["Admin", "Super Admin"]) as any,
        },
        select: ["id"],
      })

      const adminRoleIds = adminRoles.map((role) => role.id)

      if (adminRoleIds.length === 0) {
        this.logger.warn("No Admin or Super Admin roles found")
        return
      }

      // Get all Admin and Super Admin users from Auth table
      const adminUsers = await this.authRepository.find({
        where: {
          role_id: In(adminRoleIds) as any,
          deleted_at: IsNull() as any,
        },
        select: ["id"],
      })

      const adminUserIds = adminUsers.map((user) => user.id)

      // Collect all team_member_ids from all vehicles
      const allTeamMemberIds = expiringVehicles
        .map((v) => v.assigned_dispatcher_id)
        .filter((id) => id != null)

      // Create a map of team_member_id -> user_id
      const teamMemberToUserMap = new Map()

      if (allTeamMemberIds.length > 0) {
        const dispatcherUsers = await this.authRepository.find({
          where: {
            team_member_id: In(allTeamMemberIds) as any,
            deleted_at: IsNull() as any,
          },
          select: ["id", "team_member_id"],
        })

        // Populate the map
        dispatcherUsers.forEach((user) => {
          teamMemberToUserMap.set(user.team_member_id, user.id)
        })
      }

      // Process each expiring vehicle
      for (const vehicle of expiringVehicles) {
        const expiryDate = moment(vehicle.registration_expiry_date)
        const todayMoment = moment().startOf("day")
        const daysRemaining = expiryDate.diff(todayMoment, "days")

        const notificationTitle = "Vehicle Registration Expiring Soon"
        const notificationMessage = `Registration for vehicle ${vehicle.id} (${vehicle.vehicle_ownership} owned) will expire on ${expiryDate.format("DD MMM YYYY")}`

        // Collect all users to notify
        const userIdsToNotify = [...adminUserIds]

        // Add dispatcher user_id if assigned
        if (vehicle.assigned_dispatcher_id) {
          const dispatcherUserId = teamMemberToUserMap.get(
            vehicle.assigned_dispatcher_id,
          )
          if (dispatcherUserId) {
            userIdsToNotify.push(dispatcherUserId)
          } else {
            this.logger.warn(
              `No user found for team_member_id ${vehicle.assigned_dispatcher_id} for vehicle ${vehicle.car_code}`,
            )
          }
        }

        // Remove duplicates
        const uniqueUserIds = [...new Set(userIdsToNotify)]

        // Send notifications
        if (uniqueUserIds.length > 0) {
          try {
            await this.notificationService.sendNotification({
              user_ids: uniqueUserIds,
              title: notificationTitle,
              message: notificationMessage,
              data: {
                type: "registration_expiry",
                fleet_id: vehicle.id,
                car_code: vehicle.car_code,
                registration_number: vehicle.registration_number,
                expiry_date: vehicle.registration_expiry_date,
                days_remaining: daysRemaining,
              },
            })

            this.logger.log(
              `Sent expiry notification for vehicle ${vehicle.registration_number} to ${uniqueUserIds.length} user(s)`,
            )
          } catch (notificationError) {
            this.logger.error(
              `Failed to send notification for vehicle ${vehicle.registration_number}:`,
              notificationError,
            )
          }
        }
      }

      this.logger.log("Fleet registration expiry check completed successfully")
    } catch (error) {
      this.logger.error("Error in fleet registration expiry check:", error)
    }
  }
}
