import { Injectable } from "@nestjs/common"
import { ConfigService } from "@nestjs/config"
import { InjectRepository } from "@nestjs/typeorm"
import * as bcrypt from "bcrypt"
import { createUserEmail } from "src/common/emails/templates/login-credentials"
import { failureResponse, successResponse } from "src/common/response/response"
import { code } from "src/common/response/response.code"
import { messageKey } from "src/constants/message-keys"
import { STATUS } from "src/constants/trip.constant"
import { CONTACT } from "src/constants/user.constant"
import {
  dutyStatus,
  errorMessage,
  formateDate,
  generatePassword,
  isEmpty,
  sendEmailNotification,
  successMessage,
  validationMessage,
} from "src/utils/helpers"
import { Brackets, IsNull, Repository } from "typeorm"
import { ChatMessageRepository } from "../../chat/repositories/chat-message.repository"
import { ChatConversationRepository } from "../../chat/repositories/chat-conversation.repository"
import { ChatParticipantRepository } from "../../chat/repositories/chat-participant.repository"
import { SendMessageDto } from "../../chat/dto/send-message.dto"
import { ChatHistoryDto } from "../../chat/dto/chat-history.dto"
import { AuthRepository } from "../../auth/repositories/auth.repository"
import { BusinessVertical } from "../../business-verticals/entities/business-verticals.entity"
import { Department } from "../../department/entities/department.entity"
import { FleetManagement } from "../../fleet-management/entities/fleet-management.entity"
import { DriverFleetHistoryRepository } from "../../fleet-management/repositories/driver-fleet-history.repository"
import { FleetManagementRepository } from "../../fleet-management/repositories/fleet-management.repository"
import { RATING_TYPES } from "../../rating/constants/rating-type.constants"
import { RatingRepository } from "../../rating/repositories/rating.repository"
import { Role } from "../../role/entities/role.entity"
import { RoleRepository } from "../../role/repositories/role.repository"
import { TripIntermediateStop } from "../../trip-intermediate-stops/entities/trip-intermediate-stop.entity"
import { TripIntermediateStopRepository } from "../../trip-intermediate-stops/repositories/trip-intermediate-stop.repository"
import { TripFleetAssignment } from "../../trips/entities/fleet-trip-management.entity"
import { Trip } from "../../trips/entities/trip.entity"
import { DeclinedTripRepository } from "../../trips/repositories/declined-trip.repository"
import { TripCancelRepository } from "../../trips/repositories/trip-cancel-repository"
import { TripFleetRepository } from "../../trips/repositories/trip-fleet-repository"
import { TripRepository } from "../../trips/repositories/trip.repository"
import { VEHICLE_STATUS } from "../../vehicle-status/entities/vehicle-status.entity"
import { VehicleStatusService } from "../../vehicle-status/v1/vehicle-status.service"
import { VehicleHistoryQueryDto } from "../dto/drivers/driver-vehicle-history.dto"
import { FindAllDriverDto } from "../dto/drivers/find-all-drivers.dto"
import { FindAllTeamMemberDto } from "../dto/find-all-team-member.dto"
import { CreateTeamMemberDto } from "../dto/team-members/create-team-member.dto"
import { UnassignedDriversQueryDto } from "../dto/team-members/unassigned-driver.dto"
import { UpdateTeamMemberDto } from "../dto/team-members/update-team-member.dto"
import { Address } from "../entities/address.entity"
import { IdProof } from "../entities/id-proof.entity"
import { Language } from "../entities/languages.entity"
import { TeamMember } from "../entities/team_member.entity"
import { AddressRepository } from "../repositories/address.repository"
import { IdProofRepository } from "../repositories/id-proof.repository"
import { LanguagesRepository } from "../repositories/languages.repository"
import { TeamMemberRepository } from "../repositories/team_member.repository"
import moment from "moment"
import { AuthService } from "../../auth/v1/auth.service"
import { NotificationService } from "../../notification/v1/notification.service"
import { formatMessage } from "src/constants/notification-message"
import { CustomerRepository } from "../../customers/repositories/customers.repository"
import { Auth } from "../../auth/entities/auth.entity"
import { TripBasePricingRepository } from "../../trips/repositories/trip-base-price.repository"
import { ChatParticipant } from "../../chat/entities/chat-participant.entity"
import { ChatRoomRepository } from "../../chat/repositories/chat-room.repository"
import { TripTimelineRepository } from "../../trips/repositories/trip-timeline.repository"
import { TripsService } from "src/modules/trips/v1/trips.service"
import { TripAddOnsPricingRepository } from "src/modules/trips/repositories/trip-addons-pricing.repository"

@Injectable()
export class TeamMemberService {
  constructor(
    private readonly teamMemberRepository: TeamMemberRepository,
    private readonly addressRepository: AddressRepository,
    private readonly idProofRepository: IdProofRepository,
    private readonly authRepository: AuthRepository,
    private readonly roleRepository: RoleRepository,
    private readonly configService: ConfigService,
    private readonly tripFleetRepository: TripFleetRepository,
    private readonly driverFleetHistoryRepository: DriverFleetHistoryRepository,
    private readonly declinedTripRepository: DeclinedTripRepository,
    private readonly tripRepository: TripRepository,
    private readonly tripCancelRepository: TripCancelRepository,
    private readonly ratingRepository: RatingRepository,
    private readonly fleetManagementRepository: FleetManagementRepository,
    private readonly tripIntermediateStopRepository: TripIntermediateStopRepository,
    private readonly vehicleStatusService: VehicleStatusService,
    private readonly languageRepository: LanguagesRepository,
    private readonly authService: AuthService,
    private readonly notificationService: NotificationService,
    private readonly customerRepository: CustomerRepository,
    private readonly tripBasePricingRepository: TripBasePricingRepository,
    private readonly chatMessageRepository: ChatMessageRepository,
    private readonly chatConversationRepository: ChatConversationRepository,
    private readonly chatParticipantRepository: ChatParticipantRepository,
    private readonly chatRoomRepository: ChatRoomRepository,
    private readonly tripTimelineHistoryRepository: TripTimelineRepository,
    private readonly tripService: TripsService,
    private readonly tripAddOnRepository: TripBasePricingRepository,
    private readonly tripAddOnPricingRepository: TripAddOnsPricingRepository,

    @InjectRepository(TripFleetAssignment)
    private readonly tripFleetAssignmentRepository: Repository<TripFleetAssignment>,

    @InjectRepository(Trip)
    private readonly tripEntityRepository: Repository<Trip>,

    @InjectRepository(Department)
    private readonly departmentRepo: Repository<Department>,

    @InjectRepository(BusinessVertical)
    private readonly businessVerticalRepo: Repository<BusinessVertical>,

    @InjectRepository(TeamMember)
    private readonly teamMemberEntity: Repository<TeamMember>,

    @InjectRepository(Language)
    private readonly languageEntityRepository: Repository<Language>,
  ) {}

  async checkTeamMemberExist(email: string) {
    const teamMember: any = await this.teamMemberRepository.getByParams({
      where: { email },
      findOne: true,
    })
    return teamMember
  }

  async createTeamMember(
    createTeamMemberDto: CreateTeamMemberDto,
    profilePhoto?: Express.Multer.File,
    documentFiles?: Express.Multer.File[],
  ) {
    // Find role of the new member
    const currentRole = (await this.roleRepository.getByParams({
      where: { id: createTeamMemberDto.role_id },
      findOne: true,
    })) as Role | null

    if (!currentRole) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.data_not_found, {
          ":data": "Role",
        }),
      )
    }

    const isDriverOrDispatcher = ["Driver", "Dispatcher"].includes(
      currentRole.name,
    )

    // Helper function to check duplicate with cross-role logic
    const checkDuplicate = async (
      field: "email" | "phone_number",
      value: string,
      fieldLabel: string,
    ) => {
      // Get ALL existing members with this email/phone (not just first one)
      const existingMembers = (await this.teamMemberRepository.getByParams({
        where: { [field]: value },
        findOne: false, // Get all matches
        relations: ["role"],
      })) as any[]

      if (!existingMembers || existingMembers.length === 0) return null // No duplicate found

      // If current role is Driver/Dispatcher
      if (isDriverOrDispatcher) {
        // Check if any existing member has the SAME role
        const sameRoleExists = existingMembers.some(
          (member) => member.role?.name === currentRole.name,
        )

        if (sameRoleExists) {
          return failureResponse(
            code.VALIDATION,
            validationMessage(messageKey.already_exist, {
              ":data": fieldLabel,
            }),
          )
        }

        // Check if any existing member is NOT Driver/Dispatcher
        const hasNonDriverDispatcher = existingMembers.some(
          (member) => !["Driver", "Dispatcher"].includes(member.role?.name),
        )

        if (hasNonDriverDispatcher) {
          return failureResponse(
            code.VALIDATION,
            validationMessage(messageKey.already_exist, {
              ":data": fieldLabel,
            }),
          )
        }

        return null
      }

      // Current role is NOT Driver/Dispatcher - always block duplicates
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, { ":data": fieldLabel }),
      )
    }
    // Email duplicate check
    const emailError = await checkDuplicate(
      "email",
      createTeamMemberDto.email,
      "Email",
    )
    if (emailError) return emailError

    // Phone number duplicate check
    const phoneError = await checkDuplicate(
      "phone_number",
      createTeamMemberDto.phone_number,
      "Phone Number",
    )
    if (phoneError) return phoneError

    // Convert DOB if present
    if (
      createTeamMemberDto.dob &&
      typeof createTeamMemberDto.dob === "string"
    ) {
      createTeamMemberDto.dob = new Date(createTeamMemberDto.dob)
    }

    // Handle Profile Photo
    if (profilePhoto) {
      // Check if file was uploaded to R2
      if ((profilePhoto as any).storage === "R2" && (profilePhoto as any).url) {
        createTeamMemberDto.profile_photo = (profilePhoto as any).url
      } else {
        // Local storage - get relative path
        const relativePath = profilePhoto.path
          .replace(/\\/g, "/")
          .split("public/")[1]
        createTeamMemberDto.profile_photo = relativePath
      }
    }

    // Extract addresses and ID proofs
    const { addresses, id_proofs, ...teamMemberData } =
      createTeamMemberDto as any

    // Save Team Member
    const teamMember = await this.teamMemberRepository.save(teamMemberData)

    // Save Addresses (batch operation)
    if (addresses?.length > 0) {
      const addressEntities = addresses.map((addressData) => {
        const address = new Address()
        Object.assign(address, {
          ...addressData,
          team_member_id: teamMember.id,
        })
        return address
      })
      await this.addressRepository.save(addressEntities)
    }

    // Save ID Proofs (batch operation)
    if (id_proofs?.length > 0) {
      const idProofEntities = id_proofs.map((dto, i) => {
        const file = documentFiles?.[i]
        const idProof = new IdProof()
        idProof.team_member_id = teamMember.id
        idProof.document_type = dto.document_type
        idProof.document_number = dto.document_number

        if (file) {
          // Check if file was uploaded to R2
          if ((file as any).storage === "R2" && (file as any).url) {
            idProof.document_files = (file as any).url
          } else {
            // Local storage - get relative path
            idProof.document_files = file.path
              .replace(/\\/g, "/")
              .split("public/")[1]
          }
        } else {
          idProof.document_files = null
        }

        return idProof
      })
      await this.idProofRepository.save(idProofEntities)
    }

    // Fetch Complete Team Member with Relations
    const completeTeamMember = await this.teamMemberRepository.getByParams({
      where: { id: teamMember.id },
      relations: [
        "addresses",
        "id_proofs",
        "role",
        "department",
        "business_vertical",
        "reporting_to",
      ],
      findOne: true,
    })

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_add, { ":data": "team member" }),
      completeTeamMember,
    )
  }

  async findAllTeamMembers(params: FindAllTeamMemberDto) {
    const validSortColumns = [
      "id",
      "first_name",
      "last_name",
      "email",
      "created_at",
      "updated_at",
    ]

    let sortColumn: string
    let sortOrder: "ASC" | "DESC"

    if (params.sortBy && validSortColumns.includes(params.sortBy)) {
      sortColumn = params.sortBy
      sortOrder = params.sortOrder || "ASC"
    } else {
      sortColumn = "created_at"
      sortOrder = "DESC"
    }

    const filters: any = {
      departmentIds: params.department_id
        ? params.department_id.toString().split(",").map(Number)
        : undefined,
      roleIds: params.role_id
        ? params.role_id.toString().split(",").map(Number)
        : undefined,
      reportingTo: params.reporting_to ? params.reporting_to : undefined,
      status: params.status ? params.status.split(",") : undefined,
    }

    const teamMembers =
      await this.teamMemberRepository.findAllTeamMembersWithFilters({
        search: params.search,
        filters,
        take: Number(params.limit),
        skip: Number(params.skip),
        sortColumn,
        sortOrder,
      })

    // Transform document_files paths to include full URL
    if (teamMembers?.data && teamMembers.data.length > 0) {
      teamMembers.data = teamMembers.data.map((member: any) => {
        if (member.id_proofs && member.id_proofs.length > 0) {
          member.id_proofs = member.id_proofs.map((proof: any) => {
            if (proof.document_files) {
              proof.document_files = proof.document_files
            }
            return proof
          })
        }
        return member
      })
    }

    // ✅ Wrap response with successResponse
    const result = {
      count: teamMembers?.count || 0,
      data: teamMembers?.data || [],
    }

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, {
        ":data": "Team Member list",
      }),
      result,
    )
  }

  async findOneTeamMember(id: number): Promise<any> {
    const teamMember = await this.teamMemberRepository.findOneTeamMember(id)

    // Check if team member is a driver and add additional counts
    if (
      teamMember &&
      teamMember.role &&
      teamMember.role.name?.toLowerCase() === "driver"
    ) {
      // Get cancelled trips and assigned trips count
      const countsResult = await this.getCancelledTripsCount(id)
      const counts = countsResult.data

      // Get rating/review count
      const ratingResult = await this.getRating(id)
      const rating = ratingResult.data

      // Get completed trips count
      const completedTripsCount = await this.tripRepository["entity"]
        .createQueryBuilder("trip")
        .innerJoin(
          TripFleetAssignment,
          "assignment",
          "assignment.trip_id = trip.id",
        )
        .where("assignment.driver_id = :driverId", { driverId: id })
        .andWhere("trip.status = :status", { status: STATUS.COMPLETED })
        .getCount()

      // Add the counts to the team member response
      teamMember.totalReviewsCount = rating?.ratingCount || 0
      teamMember.tripCompletedCount = completedTripsCount || 0
      teamMember.totalAssignedCount = counts?.assignedTrips || 0
      teamMember.tripCancelledCount = counts?.cancelledTrips || 0
      teamMember.averageRating = rating?.averageRating || 0
    }

    return successResponse(200, "Team Member", teamMember)
  }

  // async updateTeamMember(
  //   id: number,
  //   updateTeamMemberDto: UpdateTeamMemberDto,
  //   profile_photo?: Express.Multer.File,
  //   document_files?: Express.Multer.File[],
  // ) {
  //   // Helper function to safely convert values to number or null
  //   const toNumberOrNull = (value: any): number | null => {
  //     if (
  //       value === null ||
  //       value === undefined ||
  //       value === "" ||
  //       value === "null" ||
  //       value === "undefined"
  //     ) {
  //       return null
  //     }
  //     const num = Number(value)
  //     return isNaN(num) ? null : num
  //   }

  //   // Helper function to safely convert values to string or null
  //   const toStringOrNull = (value: any): string | null => {
  //     if (
  //       value === null ||
  //       value === undefined ||
  //       value === "" ||
  //       value === "null" ||
  //       value === "undefined"
  //     ) {
  //       return null
  //     }
  //     return String(value).trim() || null
  //   }

  //   const teamMemberRaw = await this.teamMemberRepository.getByParams({
  //     where: { id },
  //     findOne: true,
  //     relations: ["addresses", "id_proofs", "role"],
  //   })

  //   if (!teamMemberRaw || Array.isArray(teamMemberRaw)) {
  //     return failureResponse(404, messageKey.data_not_found)
  //   }

  //   const teamMember = teamMemberRaw as TeamMember

  //   if (updateTeamMemberDto.email) {
  //     const existingMember = await this.checkTeamMemberExist(
  //       updateTeamMemberDto.email,
  //     )
  //     if (!isEmpty(existingMember) && existingMember.id !== id) {
  //       return failureResponse(400, messageKey.already_exist)
  //     }
  //   }

  //   const existingPhoneNumber = await this.teamMemberRepository.getByParams({
  //     where: {
  //       phone_number: updateTeamMemberDto.phone_number,
  //     },
  //     whereNotIn: {
  //       id: [id],
  //     },
  //     findOne: true,
  //   })

  //   if (!isEmpty(existingPhoneNumber)) {
  //     return failureResponse(
  //       code.VALIDATION,
  //       validationMessage(messageKey.already_exist, {
  //         ":data": "Phone Number",
  //       }),
  //     )
  //   }

  //   const userEmail = updateTeamMemberDto.email || teamMember.email
  //   const userRole = teamMember?.role_id || updateTeamMemberDto?.role_id

  //   const authRecordRaw = await this.authRepository.getByParams({
  //     where: { email: userEmail, role_id: userRole },
  //     findOne: true,
  //   })

  //   const authRecord =
  //     !authRecordRaw || Array.isArray(authRecordRaw)
  //       ? null
  //       : (authRecordRaw as any)

  //   let generatedPassword: string | null = null
  //   let shouldSendEmail = false

  //   const roleIdToUse =
  //     updateTeamMemberDto.role_id !== undefined
  //       ? updateTeamMemberDto.role_id
  //       : teamMember.role?.id

  //   if (updateTeamMemberDto?.status == "inactive") {
  //     const driverRole = (await this.roleRepository.getByParams({
  //       whereLower: { name: "Driver" },
  //       findOne: true,
  //     })) as any

  //     if (teamMember.role_id === driverRole.id) {
  //       const assignedFleet = await this.fleetManagementRepository.getByParams({
  //         where: { assigned_dispatcher_id: id },
  //         findOne: true,
  //       })

  //       const assignedTrips = await this.tripFleetRepository.getByParams({
  //         where: { driver_id: id },
  //         whereNull: ["trip_completed_at", "released_at"],
  //         whereNotNull: ["trip_id"],
  //         findOne: true,
  //       })

  //       if (assignedFleet || assignedTrips) {
  //         return failureResponse(
  //           400,
  //           "You cannot deactivate a driver with assigned fleet or trips",
  //         )
  //       }
  //     }
  //   }

  //   const statusToUse =
  //     updateTeamMemberDto.status !== undefined
  //       ? updateTeamMemberDto.status
  //       : teamMember.status

  //   let relativePath = null
  //   if (profile_photo && profile_photo.path) {
  //     relativePath = profile_photo.path.replace(/\\/g, "/").split("public/")[1]
  //     updateTeamMemberDto.profile_photo = relativePath
  //   } else {
  //     updateTeamMemberDto.profile_photo = teamMember.profile_photo
  //   }

  //   if (!authRecord && !isEmpty(updateTeamMemberDto?.role_id)) {
  //     generatedPassword = generatePassword()
  //     const hashedPassword = await bcrypt.hash(generatedPassword, 10)

  //     await this.authRepository.save({
  //       email: userEmail,
  //       password: hashedPassword,
  //       first_name: updateTeamMemberDto.first_name || teamMember.first_name,
  //       last_name: updateTeamMemberDto.last_name || teamMember.last_name,
  //       country_code:
  //         updateTeamMemberDto.country_code || teamMember.country_code,
  //       contact_no: updateTeamMemberDto.phone_number || teamMember.phone_number,
  //       role_id: roleIdToUse,
  //       team_member_id: id,
  //       status: statusToUse,
  //       profile_pic: relativePath,
  //     })
  //     shouldSendEmail = true
  //   } else if (authRecord) {
  //     authRecord.role_id = roleIdToUse
  //     authRecord.status = statusToUse
  //     authRecord.first_name =
  //       updateTeamMemberDto.first_name || teamMember.first_name
  //     authRecord.last_name =
  //       updateTeamMemberDto.last_name || teamMember.last_name
  //     authRecord.profile_pic = relativePath
  //     await this.authRepository.save(authRecord)
  //   }

  //   try {
  //     if (profile_photo) {
  //       const relativePath = profile_photo.path
  //         .replace(/\\/g, "/")
  //         .split("public/")[1]
  //       updateTeamMemberDto.profile_photo = relativePath
  //     }

  //     const { addresses, id_proofs, ...rest } = updateTeamMemberDto

  //     if (updateTeamMemberDto.department?.length) {
  //       const departments = await this.departmentRepo.findByIds(
  //         updateTeamMemberDto.department,
  //       )
  //       teamMember.department = departments
  //     }

  //     if (updateTeamMemberDto.business_vertical?.length) {
  //       const verticals = await this.businessVerticalRepo.findByIds(
  //         updateTeamMemberDto.business_vertical,
  //       )
  //       teamMember.business_vertical = verticals
  //     }

  //     if (updateTeamMemberDto.languages?.length) {
  //       const languages = await this.languageEntityRepository.findByIds(
  //         updateTeamMemberDto.languages.map(Number),
  //       )
  //       teamMember.languages = languages
  //     }

  //     if (document_files && document_files.length > 0) {
  //       document_files.forEach((file, index) => {
  //         console.log(`Document file ${index}:`, {
  //           fieldname: file.fieldname,
  //           originalname: file.originalname,
  //           mimetype: file.mimetype,
  //           size: file.size,
  //         })
  //       })
  //     }

  //     const teamMemberData = {
  //       ...rest,
  //       role_id: roleIdToUse,
  //       team_member_id: id,
  //       current_step: rest.current_step
  //         ? Number(rest.current_step)
  //         : teamMember.current_step,
  //       form_status: rest.form_status || teamMember.form_status,
  //       department: teamMember.department || rest.department,
  //       business_vertical:
  //         teamMember.business_vertical || rest.business_vertical,
  //       languages: teamMember.languages || rest.languages,
  //       // Clean up emergency contact fields
  //       emergency_contact_name:
  //         toStringOrNull(rest.emergency_contact_name) ||
  //         teamMember.emergency_contact_name,
  //       emergency_contact_code:
  //         toStringOrNull(rest.emergency_contact_code) ||
  //         teamMember.emergency_contact_code,
  //       emergency_contact_number:
  //         toStringOrNull(rest.emergency_contact_number) ||
  //         teamMember.emergency_contact_number,
  //     }

  //     const updatedRole = (await this.roleRepository.getByParams({
  //       where: { id: roleIdToUse },
  //       findOne: true,
  //     })) as Role

  //     teamMember.role = updatedRole
  //     Object.assign(teamMember, teamMemberData)
  //     await this.teamMemberRepository.save(teamMember)

  //     // Handle addresses
  //     if (addresses && addresses.length > 0) {
  //       const addressTypes = new Set()
  //       for (const addr of addresses) {
  //         if (addressTypes.has(addr.address_type)) {
  //           return failureResponse(
  //             400,
  //             `Duplicate address type: ${addr.address_type}`,
  //           )
  //         }
  //         addressTypes.add(addr.address_type)
  //       }

  //       const existingAddresses = teamMember.addresses || []

  //       for (const addressData of addresses) {
  //         // Skip addresses with all null/empty values
  //         const hasAnyValue =
  //           toStringOrNull(addressData.address_line_1) ||
  //           toNumberOrNull(addressData.country_id) ||
  //           toNumberOrNull(addressData.state_id) ||
  //           toNumberOrNull(addressData.city_id) ||
  //           toStringOrNull(addressData.zipcode)

  //         if (!hasAnyValue) {
  //           // If there's an existing address of this type with no values, delete it
  //           const existingAddress = existingAddresses.find(
  //             (addr) => addr.address_type === addressData.address_type,
  //           )
  //           if (existingAddress) {
  //             await this.addressRepository.remove(existingAddress as any)
  //           }
  //           continue
  //         }

  //         // Clean the address data
  //         const cleanedAddressData = {
  //           address_type: addressData.address_type,
  //           address_line_1: toStringOrNull(addressData.address_line_1),
  //           // country: toStringOrNull(addressData.country),
  //           country_id: toNumberOrNull(addressData.country_id),
  //           // state: toStringOrNull(addressData.state),
  //           state_id: toNumberOrNull(addressData.state_id),
  //           // city: toStringOrNull(addressData.city),
  //           city_id: toNumberOrNull(addressData.city_id),
  //           zipcode: toStringOrNull(addressData.zipcode),
  //           team_member_id: id,
  //           is_emergency_contact:
  //             addressData.address_type === "emergency_contact",
  //         }

  //         const existingAddress = existingAddresses.find(
  //           (addr) => addr.address_type === addressData.address_type,
  //         )

  //         if (existingAddress) {
  //           Object.assign(existingAddress, cleanedAddressData)
  //           await this.addressRepository.save(existingAddress)
  //         } else {
  //           const address = new Address()
  //           Object.assign(address, cleanedAddressData)
  //           await this.addressRepository.save(address)
  //         }
  //       }

  //       const addressTypesToKeep = addresses.map((addr) => addr.address_type)
  //       const addressesToRemove = existingAddresses.filter(
  //         (addr) => !addressTypesToKeep.includes(addr.address_type as any),
  //       )

  //       if (addressesToRemove.length > 0) {
  //         await this.addressRepository.remove(addressesToRemove as any)
  //       }
  //     }

  //     // Handle ID Proofs
  //     if (id_proofs && id_proofs.length > 0) {
  //       const documentTypes = new Set()
  //       for (const proof of id_proofs) {
  //         if (documentTypes.has(proof.document_type)) {
  //           return failureResponse(
  //             400,
  //             `Duplicate document type: ${proof.document_type}`,
  //           )
  //         }
  //         documentTypes.add(proof.document_type)
  //       }

  //       const existingIdProofs = teamMember.id_proofs || []

  //       const filesByName = new Map<string, Express.Multer.File>()
  //       if (document_files && document_files.length > 0) {
  //         document_files.forEach((file) => {
  //           console.log(
  //             `Processing file: ${file.originalname}, fieldname: ${file.fieldname}`,
  //           )
  //           const fileName = file.originalname.toLowerCase()
  //           filesByName.set(fileName, file)

  //           for (const proof of id_proofs) {
  //             const docType = proof.document_type
  //               .toLowerCase()
  //               .replace("/", "_")
  //             if (fileName.includes(docType)) {
  //               filesByName.set(proof.document_type, file)
  //               break
  //             }
  //           }
  //         })
  //       }

  //       for (const proofData of id_proofs) {
  //         const existingProof = existingIdProofs.find(
  //           (proof) => proof.document_type === proofData.document_type,
  //         )

  //         let matchedFile = filesByName.get(proofData.document_type)
  //         if (!matchedFile) {
  //           for (const [fileName, file] of filesByName.entries()) {
  //             const docType = proofData.document_type
  //               .toLowerCase()
  //               .replace("/", "_")
  //             if (fileName.includes(docType)) {
  //               matchedFile = file
  //               break
  //             }
  //           }
  //         }

  //         let documentFilePath: string | null = null
  //         if (matchedFile) {
  //           documentFilePath = matchedFile.path
  //             .replace(/\\/g, "/")
  //             .split("public/")[1]
  //         }

  //         if (existingProof) {
  //           const updatedData = { ...proofData }
  //           if (documentFilePath) {
  //             updatedData.document_files = documentFilePath
  //           } else {
  //             updatedData.document_files = existingProof.document_files
  //           }
  //           Object.assign(existingProof, updatedData)
  //           await this.idProofRepository.save(existingProof)
  //         } else {
  //           const idProof = new IdProof()
  //           const newProofData = {
  //             ...proofData,
  //             team_member_id: id,
  //             document_files: documentFilePath,
  //           }
  //           Object.assign(idProof, newProofData)
  //           await this.idProofRepository.save(idProof)
  //           console.log(`Created new ID proof for ${proofData.document_type}`)
  //         }
  //       }

  //       const documentTypesToKeep = id_proofs.map(
  //         (proof) => proof.document_type,
  //       )
  //       const proofsToRemove = existingIdProofs.filter(
  //         (proof) => !documentTypesToKeep.includes(proof.document_type as any),
  //       )

  //       if (proofsToRemove.length > 0) {
  //         for (const proof of proofsToRemove) {
  //           console.log(`Removing ID proof: ${proof.document_type}`)
  //           await this.idProofRepository.remove({ id: proof.id })
  //         }
  //       }
  //     }

  //     // Send credentials email if needed
  //     if (
  //       shouldSendEmail &&
  //       generatedPassword &&
  //       teamMember.role.is_web_login === true
  //     ) {
  //       console.log("mail has been send successfully")
  //       const emailHTML = createUserEmail(generatedPassword)
  //       await sendEmailNotification(
  //         userEmail,
  //         emailHTML,
  //         "Your account credentials have been created",
  //       )
  //     }

  //     const updatedTeamMember = await this.teamMemberRepository.getByParams({
  //       where: { id },
  //       findOne: true,
  //       relations: [
  //         "addresses",
  //         "id_proofs",
  //         "role",
  //         "department",
  //         "business_vertical",
  //         "reporting_to",
  //       ],
  //     })

  //     return successResponse(
  //       code.SUCCESS,
  //       successMessage(messageKey.data_update, {
  //         ":data": "team member",
  //       }),
  //       updatedTeamMember,
  //     )
  //   } catch (error) {
  //     console.error(error, "errorrrrrrrrrrr")
  //     return failureResponse(code.ERROR, messageKey.something_went_wrong)
  //   }
  // }

  async updateTeamMember(
    id: number,
    updateTeamMemberDto: UpdateTeamMemberDto,
    profile_photo?: Express.Multer.File,
    document_files?: Express.Multer.File[],
  ) {
    const toNumberOrNull = (value: any): number | null => {
      if (
        value === null ||
        value === undefined ||
        value === "" ||
        value === "null" ||
        value === "undefined"
      ) {
        return null
      }
      const num = Number(value)
      return isNaN(num) ? null : num
    }

    const toStringOrNull = (value: any): string | null => {
      if (
        value === null ||
        value === undefined ||
        value === "" ||
        value === "null" ||
        value === "undefined"
      ) {
        return null
      }
      return String(value).trim() || null
    }

    const teamMemberRaw = await this.teamMemberRepository.getByParams({
      where: { id },
      findOne: true,
      relations: ["addresses", "id_proofs", "role"],
    })

    if (!teamMemberRaw || Array.isArray(teamMemberRaw)) {
      return failureResponse(404, messageKey.data_not_found)
    }

    const teamMember = teamMemberRaw as TeamMember

    const originalPhoneNumber = teamMember.phone_number

    // Determine the role to use for validation
    const roleIdToUse =
      updateTeamMemberDto.role_id !== undefined
        ? updateTeamMemberDto.role_id
        : teamMember.role?.id

    // Get the current role
    const currentRole = (await this.roleRepository.getByParams({
      where: { id: roleIdToUse },
      findOne: true,
    })) as Role

    if (!currentRole) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.data_not_found, {
          ":data": "Role",
        }),
      )
    }

    const isDriverOrDispatcher = ["Driver", "Dispatcher"].includes(
      currentRole.name,
    )

    // Email validation with cross-role logic
    if (updateTeamMemberDto.email) {
      const existingMember = (await this.teamMemberRepository.getByParams({
        where: { email: updateTeamMemberDto.email },
        whereNotIn: { id: [id] },
        findOne: true,
        relations: ["role"],
      })) as any

      if (!isEmpty(existingMember)) {
        // If current role is Driver/Dispatcher
        if (isDriverOrDispatcher) {
          const existingIsDriverOrDispatcher = [
            "Driver",
            "Dispatcher",
          ].includes(existingMember.role?.name)

          if (existingIsDriverOrDispatcher) {
            // Both are Driver/Dispatcher - only block if SAME role
            if (existingMember.role?.name === currentRole.name) {
              return failureResponse(
                code.VALIDATION,
                validationMessage(messageKey.already_exist, {
                  ":data": "Email",
                }),
              )
            }
            // Different roles (Driver vs Dispatcher) - allow it
          } else {
            // Existing is NOT Driver/Dispatcher - block it
            return failureResponse(
              code.VALIDATION,
              validationMessage(messageKey.already_exist, {
                ":data": "Email",
              }),
            )
          }
        } else {
          // Current role is NOT Driver/Dispatcher - always block duplicates
          return failureResponse(
            code.VALIDATION,
            validationMessage(messageKey.already_exist, {
              ":data": "Email",
            }),
          )
        }
      }
    }

    // Phone number validation with cross-role logic
    if (updateTeamMemberDto.phone_number) {
      const existingPhoneNumber = (await this.teamMemberRepository.getByParams({
        where: {
          phone_number: updateTeamMemberDto.phone_number,
        },
        whereNotIn: {
          id: [id],
        },
        findOne: true,
        relations: ["role"],
      })) as any

      if (!isEmpty(existingPhoneNumber)) {
        // If current role is Driver/Dispatcher
        if (isDriverOrDispatcher) {
          const existingIsDriverOrDispatcher = [
            "Driver",
            "Dispatcher",
          ].includes(existingPhoneNumber.role?.name)

          if (existingIsDriverOrDispatcher) {
            // Both are Driver/Dispatcher - only block if SAME role
            if (existingPhoneNumber.role?.name === currentRole.name) {
              return failureResponse(
                code.VALIDATION,
                validationMessage(messageKey.already_exist, {
                  ":data": "Phone Number",
                }),
              )
            }
            // Different roles (Driver vs Dispatcher) - allow it
          } else {
            // Existing is NOT Driver/Dispatcher - block it
            return failureResponse(
              code.VALIDATION,
              validationMessage(messageKey.already_exist, {
                ":data": "Phone Number",
              }),
            )
          }
        } else {
          // Current role is NOT Driver/Dispatcher - always block duplicates
          return failureResponse(
            code.VALIDATION,
            validationMessage(messageKey.already_exist, {
              ":data": "Phone Number",
            }),
          )
        }
      }
    }

    const userEmail = updateTeamMemberDto.email || teamMember.email
    const userRole = teamMember?.role_id || updateTeamMemberDto?.role_id

    const authRecordRaw = await this.authRepository.getByParams({
      where: { email: userEmail, role_id: userRole },
      findOne: true,
    })

    const authRecord =
      !authRecordRaw || Array.isArray(authRecordRaw)
        ? null
        : (authRecordRaw as any)

    let generatedPassword: string | null = null
    let shouldSendEmail = false

    if (updateTeamMemberDto?.status == "inactive") {
      const driverRole = (await this.roleRepository.getByParams({
        whereLower: { name: "Driver" },
        findOne: true,
      })) as any

      if (teamMember.role_id === driverRole.id) {
        const assignedFleet = await this.fleetManagementRepository.getByParams({
          where: { assigned_dispatcher_id: id },
          findOne: true,
        })

        const assignedTrips = await this.tripFleetRepository.getByParams({
          where: { driver_id: id },
          whereNull: ["trip_completed_at", "released_at"],
          whereNotNull: ["trip_id"],
          findOne: true,
        })

        if (assignedFleet || assignedTrips) {
          return failureResponse(
            400,
            "You cannot deactivate a driver with assigned fleet or trips",
          )
        }
      }
    }

    const statusToUse =
      updateTeamMemberDto.status !== undefined
        ? updateTeamMemberDto.status
        : teamMember.status

    let relativePath = null
    if (profile_photo && profile_photo.path) {
      relativePath = profile_photo.path.replace(/\\/g, "/").split("public/")[1]
      updateTeamMemberDto.profile_photo = relativePath
    } else {
      updateTeamMemberDto.profile_photo = teamMember.profile_photo
    }

    if (!authRecord && !isEmpty(updateTeamMemberDto?.role_id)) {
      generatedPassword = generatePassword()
      const hashedPassword = await bcrypt.hash(generatedPassword, 10)

      await this.authRepository.save({
        email: userEmail,
        password: hashedPassword,
        first_name: updateTeamMemberDto.first_name || teamMember.first_name,
        last_name: updateTeamMemberDto.last_name || teamMember.last_name,
        country_code:
          updateTeamMemberDto.country_code || teamMember.country_code,
        contact_no: updateTeamMemberDto.phone_number || teamMember.phone_number,
        role_id: roleIdToUse,
        team_member_id: id,
        status: statusToUse,
        profile_pic: relativePath,
      })

      shouldSendEmail = true
    } else if (authRecord) {
      authRecord.role_id = roleIdToUse
      authRecord.status = statusToUse
      authRecord.first_name =
        updateTeamMemberDto.first_name || teamMember.first_name
      authRecord.last_name =
        updateTeamMemberDto.last_name || teamMember.last_name
      authRecord.country_code =
        updateTeamMemberDto.country_code || teamMember.country_code
      authRecord.contact_no =
        updateTeamMemberDto.phone_number || teamMember.phone_number
      authRecord.profile_pic = relativePath
      await this.authRepository.save(authRecord)
    }

    try {
      if (profile_photo) {
        // Check if file was uploaded to R2
        if (
          (profile_photo as any).storage === "R2" &&
          (profile_photo as any).url
        ) {
          updateTeamMemberDto.profile_photo = (profile_photo as any).url
        } else {
          // Local storage - get relative path
          const relativePath = profile_photo.path
            .replace(/\\/g, "/")
            .split("public/")[1]
          updateTeamMemberDto.profile_photo = relativePath
        }
      }

      const { addresses, id_proofs, ...rest } = updateTeamMemberDto

      if (updateTeamMemberDto.department?.length) {
        const departments = await this.departmentRepo.findByIds(
          updateTeamMemberDto.department,
        )
        teamMember.department = departments
      }

      if (updateTeamMemberDto.business_vertical?.length) {
        const verticals = await this.businessVerticalRepo.findByIds(
          updateTeamMemberDto.business_vertical,
        )
        teamMember.business_vertical = verticals
      }

      if (
        Array.isArray(updateTeamMemberDto.languages) &&
        updateTeamMemberDto.languages?.length > 0
      ) {
        const languages = await this.languageEntityRepository.findByIds(
          updateTeamMemberDto.languages.map(Number),
        )
        teamMember.languages = languages
      } else if (
        updateTeamMemberDto?.current_step === 4 &&
        isEmpty(teamMember.languages)
      ) {
        teamMember.languages = []
      }

      if (document_files && document_files.length > 0) {
        document_files.forEach((file, index) => {
          console.log(`Document file ${index}:`, {
            fieldname: file.fieldname,
            originalname: file.originalname,
            mimetype: file.mimetype,
            size: file.size,
          })
        })
      }

      const teamMemberData = {
        ...rest,
        role_id: roleIdToUse,
        team_member_id: id,
        current_step: rest.current_step
          ? Number(rest.current_step)
          : teamMember.current_step,
        form_status: rest.form_status || teamMember.form_status,
        department: teamMember.department || rest.department,
        business_vertical:
          teamMember.business_vertical || rest.business_vertical,
        languages: teamMember.languages || rest.languages,
        // Clean up emergency contact fields
        emergency_contact_name:
          toStringOrNull(rest.emergency_contact_name) ||
          teamMember.emergency_contact_name,
        emergency_contact_code:
          toStringOrNull(rest.emergency_contact_code) ||
          teamMember.emergency_contact_code,
        emergency_contact_number:
          toStringOrNull(rest.emergency_contact_number) ||
          teamMember.emergency_contact_number,
      }

      const updatedRole = (await this.roleRepository.getByParams({
        where: { id: roleIdToUse },
        findOne: true,
      })) as Role

      teamMember.role = updatedRole
      Object.assign(teamMember, teamMemberData)
      await this.teamMemberRepository.save(teamMember)
      // If phone number used for login changed, logout all linked users (all roles) from all sessions
      const phoneChanged =
        typeof updateTeamMemberDto.phone_number !== "undefined" &&
        updateTeamMemberDto.phone_number !== originalPhoneNumber

      if (phoneChanged) {
        const linkedUsers = (await this.authRepository.getByParams({
          where: { team_member_id: id },
        })) as Auth[] | Auth | null

        if (Array.isArray(linkedUsers)) {
          for (const u of linkedUsers) {
            await this.authService.logoutAllSessionsByUserId(u.id)
          }
        } else if (linkedUsers && (linkedUsers as any).id) {
          await this.authService.logoutAllSessionsByUserId(
            (linkedUsers as any).id,
          )
        }
      }

      // ✅ Address update logic
      if (addresses && addresses.length > 0) {
        const seenTypes = new Set()
        for (const addr of addresses) {
          if (seenTypes.has(addr.address_type)) {
            return failureResponse(
              400,
              `Duplicate address type: ${addr.address_type}`,
            )
          }
          seenTypes.add(addr.address_type)
        }

        const existingAddresses = (await this.addressRepository.getByParams({
          where: { team_member_id: id },
        })) as any[]

        for (const addressData of addresses) {
          if (addressData.id) {
            // ✅ update exact row including null values
            const row = existingAddresses.find((a) => a.id === addressData.id)
            if (row) {
              row.address_line_1 = toStringOrNull(addressData.address_line_1)
              row.country_id = toNumberOrNull(addressData.country_id)
              row.state_id = toNumberOrNull(addressData.state_id)
              row.city_id = toNumberOrNull(addressData.city_id)
              row.zipcode = toStringOrNull(addressData.zipcode)
              row.address_type = addressData.address_type
              row.is_emergency_contact =
                addressData.address_type === "emergency_contact"
              await this.addressRepository.save(row)
            }
            continue
          }

          // ✅ if no new id given, update row by type instead of insert
          const rowByType = existingAddresses.find(
            (a) => a.address_type === addressData.address_type,
          )
          if (rowByType) {
            rowByType.address_line_1 = toStringOrNull(
              addressData.address_line_1,
            )
            rowByType.country_id = toNumberOrNull(addressData.country_id)
            rowByType.state_id = toNumberOrNull(addressData.state_id)
            rowByType.city_id = toNumberOrNull(addressData.city_id)
            rowByType.zipcode = toStringOrNull(addressData.zipcode)
            await this.addressRepository.save(rowByType)
          } else {
            // original insert kept if type row not exists
            const newAddress = new Address()
            Object.assign(newAddress, {
              address_line_1: toStringOrNull(addressData.address_line_1),
              country_id: toNumberOrNull(addressData.country_id),
              state_id: toNumberOrNull(addressData.state_id),
              city_id: toNumberOrNull(addressData.city_id),
              zipcode: toStringOrNull(addressData.zipcode),
              address_type: addressData.address_type,
              team_member_id: id,
              is_emergency_contact:
                addressData.address_type === "emergency_contact",
            })
            await this.addressRepository.save(newAddress)
          }
        }
      }

      // Handle ID Proofs
      if (id_proofs && id_proofs.length > 0) {
        const documentTypes = new Set()
        for (const proof of id_proofs) {
          if (documentTypes.has(proof.document_type)) {
            return failureResponse(
              400,
              `Duplicate document type: ${proof.document_type}`,
            )
          }
          documentTypes.add(proof.document_type)
        }

        const existingIdProofs = teamMember.id_proofs || []

        const filesByName = new Map<string, Express.Multer.File>()
        if (document_files && document_files.length > 0) {
          document_files.forEach((file) => {
            console.log(
              `Processing file: ${file.originalname}, fieldname: ${file.fieldname}`,
            )
            const fileName = file.originalname.toLowerCase()
            filesByName.set(fileName, file)

            for (const proof of id_proofs) {
              const docType = proof.document_type
                .toLowerCase()
                .replace("/", "_")
              if (fileName.includes(docType)) {
                filesByName.set(proof.document_type, file)
                break
              }
            }
          })
        }

        for (const proofData of id_proofs) {
          const existingProof = existingIdProofs.find(
            (proof) => proof.document_type === proofData.document_type,
          )

          let matchedFile = filesByName.get(proofData.document_type)
          if (!matchedFile) {
            for (const [fileName, file] of filesByName.entries()) {
              const docType = proofData.document_type
                .toLowerCase()
                .replace("/", "_")
              if (fileName.includes(docType)) {
                matchedFile = file
                break
              }
            }
          }

          let documentFilePath: string | null = null
          if (matchedFile) {
            // Check if file was uploaded to R2
            if (
              (matchedFile as any).storage === "R2" &&
              (matchedFile as any).url
            ) {
              documentFilePath = (matchedFile as any).url
            } else {
              // Local storage - get relative path
              documentFilePath = matchedFile.path
                .replace(/\\/g, "/")
                .split("public/")[1]
            }
          }

          if (existingProof) {
            const updatedData = { ...proofData }
            if (documentFilePath) {
              updatedData.document_files = documentFilePath
            } else {
              updatedData.document_files = existingProof.document_files
            }
            Object.assign(existingProof, updatedData)
            await this.idProofRepository.save(existingProof)
          } else {
            const idProof = new IdProof()
            const newProofData = {
              ...proofData,
              team_member_id: id,
              document_files: documentFilePath,
            }
            Object.assign(idProof, newProofData)
            await this.idProofRepository.save(idProof)
            console.log(`Created new ID proof for ${proofData.document_type}`)
          }
        }

        const documentTypesToKeep = id_proofs.map(
          (proof) => proof.document_type,
        )
        const proofsToRemove = existingIdProofs.filter(
          (proof) => !documentTypesToKeep.includes(proof.document_type as any),
        )

        if (proofsToRemove.length > 0) {
          for (const proof of proofsToRemove) {
            console.log(`Removing ID proof: ${proof.document_type}`)
            await this.idProofRepository.remove({ id: proof.id })
          }
        }
      }

      // Send credentials email if needed
      if (
        shouldSendEmail &&
        generatedPassword &&
        teamMember.role.is_web_login === true
      ) {
        console.log("mail has been send successfully")
        const emailHTML = createUserEmail(generatedPassword)
        await sendEmailNotification(
          userEmail,
          emailHTML,
          "Your account credentials have been created",
        )
      }

      const updatedTeamMember = await this.teamMemberRepository.getByParams({
        where: { id },
        findOne: true,
        relations: [
          "addresses",
          "id_proofs",
          "role",
          "department",
          "business_vertical",
          "reporting_to",
        ],
      })

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_update, {
          ":data": "team member",
        }),
        updatedTeamMember,
      )
    } catch (error) {
      console.error(error, "errorrrrrrrrrrr")
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }

  async remove(id: number) {
    try {
      const teamMember = (await this.teamMemberRepository.getByParams({
        where: { id },
        findOne: true,
      })) as any

      if (isEmpty(teamMember)) {
        return failureResponse(404, messageKey.data_not_found)
      }

      if (teamMember.email) {
        const user = (await this.authRepository.getByParams({
          where: { email: teamMember.email },
          findOne: true,
        })) as any

        if (user) {
          await this.authRepository.remove({ id: user.id })
        }
      }

      await this.teamMemberRepository.remove({ id })

      return successResponse(200, messageKey.data_removed)
    } catch (error) {
      return failureResponse(500, messageKey.something_went_wrong)
    }
  }

  async deleteIdProof(teamMemberId: number, idProofId: number) {
    const idProofResult = await this.idProofRepository.getByParams({
      where: {
        id: idProofId,
        team_member_id: teamMemberId,
      },
      findOne: true,
    })

    if (isEmpty(idProofResult)) {
      return failureResponse(404, messageKey.data_not_found)
    }
    await this.idProofRepository.remove({ id: idProofId })
    return successResponse(code.SUCCESS, messageKey.data_removed)
  }

  async getReportingTeamMembersByRole(roleId: number) {
    try {
      const reportingRole: any = await this.roleRepository.getByParams({
        where: { id: roleId },
        findOne: true,
        select: ["id", "name", "parent_role_id"],
      })

      const reportingTeamMembers = await this.teamMemberRepository.getByParams({
        where: { role_id: reportingRole?.parent_role_id },
        select: ["id", "first_name", "last_name"],
      })

      if (isEmpty(reportingTeamMembers)) {
        return failureResponse(404, messageKey.data_not_found)
      }

      return successResponse(
        200,
        messageKey.data_retrieve,
        reportingTeamMembers,
      )
    } catch (error) {
      return failureResponse(500, messageKey.something_went_wrong)
    }
  }

  async getDriversByRole() {
    try {
      const driverRole = (await this.roleRepository.getByParams({
        whereLower: { name: "Driver" },
        findOne: true,
      })) as any

      if (!driverRole) {
        return failureResponse(404, messageKey.data_not_found)
      }

      const drivers = await this.teamMemberRepository.getByParams({
        select: ["id", "first_name", "last_name", "email", "phone_number"],
        where: {
          role_id: driverRole.id,
        },
        relations: ["role:id,name"],
      })

      if (isEmpty(drivers)) {
        return failureResponse(404, messageKey.data_not_found)
      }

      return successResponse(200, messageKey.data_retrieve, drivers)
    } catch (error) {
      console.error(error)
      return failureResponse(500, messageKey.something_went_wrong)
    }
  }

  async getDispatchersByRole() {
    try {
      const dispatcherRole = (await this.roleRepository.getByParams({
        whereLower: { name: "Dispatcher" },
        findOne: true,
      })) as any

      if (!dispatcherRole) {
        return failureResponse(404, messageKey.data_not_found)
      }

      const dispatchers = await this.teamMemberRepository.getByParams({
        select: ["id", "first_name", "last_name", "email", "phone_number"],
        where: {
          role_id: dispatcherRole.id,
        },
        relations: ["role:id,name"],
      })

      if (isEmpty(dispatchers)) {
        return failureResponse(code.DATA_NOT_FOUND, messageKey.data_not_found)
      }

      return successResponse(
        code.SUCCESS,
        messageKey.data_retrieve,
        dispatchers,
      )
    } catch (error) {
      console.error(error)
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }

  async getDispatchersWiseDrivers(params: FindAllDriverDto) {
    try {
      const where: any = {}

      if (params.role_id) {
        where.role = { id: params.role_id }
      }

      const drivers = await this.teamMemberRepository.getByParams({
        where,
        select: ["id", "first_name", "last_name", "email", "phone_number"],
        relations: ["role", "reporting_to"],
      })
      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, {
          ":data": "drivers",
        }),
        drivers,
      )
    } catch (error) {
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }

  async getUnassignedDrivers(query: UnassignedDriversQueryDto) {
    const driverROleId: any = await this.roleRepository.getByParams({
      where: {
        name: "driver",
      },
      findOne: true,
      select: ["id"],
    })

    if (isEmpty(driverROleId)) {
      return failureResponse(
        code.VALIDATION,
        errorMessage(messageKey.data_not_found, {
          ":data": "Trip",
        }),
      )
    }

    const { skip = 0, limit = 10, search } = query

    const qb = this.teamMemberEntity
      .createQueryBuilder("tm")
      .select(["tm.id", "tm.first_name", "tm.last_name"])
      .where("tm.role_id = :driverRoleId", { driverRoleId: driverROleId?.id })
      .andWhere((subQb) => {
        const subQuery = subQb
          .subQuery()
          .select("fleet.assigned_driver")
          .from(FleetManagement, "fleet")
          .where("fleet.assigned_driver IS NOT NULL")
          .getQuery()
        return "tm.id NOT IN " + subQuery
      })

    if (search) {
      const searchValue = `%${String(search)}%`
      qb.andWhere(
        new Brackets((qb) => {
          qb.where(`tm.first_name ILIKE :search`, { search: searchValue })
            .orWhere(`tm.last_name ILIKE :search`, { search: searchValue })
            .orWhere(`CONCAT(tm.first_name, ' ', tm.last_name) ILIKE :search`, {
              search: searchValue,
            })
        }),
      )
    }

    qb.skip(skip).take(limit)

    const [data, count] = await qb.getManyAndCount()

    const result = {
      count,
      data,
    }
    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve),
      result,
    )
  }

  async getDriverAssignVehicle(
    driver_id: number,
    vehicleHistoryQueryDto: VehicleHistoryQueryDto,
  ) {
    const limit =
      vehicleHistoryQueryDto.limit ||
      this.configService.get<number>("APP.pagination.take")
    const skip =
      vehicleHistoryQueryDto.skip ||
      this.configService.get<number>("APP.pagination.skip")

    const driverVehicleHistory =
      await this.driverFleetHistoryRepository.getByParams({
        take: limit,
        skip,
        where: {
          driver_id: driver_id,
        },
        orderBy: { released_at: "DESC" },
        select: ["id", "assigned_at", "released_at", "created_at"],
        relations: [
          "fleet:id,car_code,year,color,registration_number,vin_number.vehicle_type:id,name",
        ],
      })

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, { ":data": "Trips" }),
      driverVehicleHistory,
    )
  }

  async getDriverProfileDeatails(driverId: number) {
    try {
      const driver = (await this.teamMemberRepository.getByParams({
        where: { id: driverId },
        relations: [
          "role:id,name",
          "reporting_to:id,first_name,last_name,country_code,phone_number,profile_photo",
          "id_proofs:id,document_type,document_number,expiry_date,document_files",
          "languages:id,code,name",
        ],
        select: [
          "id",
          "first_name",
          "last_name",
          "email",
          "phone_number",
          "profile_photo",
          "country_code",
          "duty_status",
        ],
        findOne: true,
      })) as any

      if (!driver) {
        return failureResponse(
          code.DATA_NOT_FOUND,
          errorMessage(messageKey.data_not_found, { ":data": "Driver" }),
        )
      }

      const vehicleDetails =
        await this.driverFleetHistoryRepository.findTripsByDriverId(driverId)

      // Get one ongoing trip ID for the driver
      const ongoingTrip = await this.tripFleetAssignmentRepository
        .createQueryBuilder("tfa")
        .select("tfa.trip_id")
        .innerJoin("tfa.trip", "t")
        .where("tfa.driver_id = :driverId", { driverId })
        .andWhere("t.status = :status", { status: STATUS.ONGOING })
        .andWhere("tfa.released_at IS NULL")
        .andWhere("tfa.trip_completed_at IS NULL")
        .andWhere("t.deleted_at IS NULL")
        .orderBy("t.pickup_datetime", "ASC")
        .limit(1)
        .getRawOne()

      // Removed BACK_URL concatenation from id_proofs document_files
      // (Now handled after load)

      const driving_license = driver.id_proofs.find(
        (proof) => proof.document_type === "driving-license",
      )

      const registration_doc = driver.id_proofs.find(
        (proof) => proof.document_type === "registration",
      )

      const latestInsurance = []

      if (vehicleDetails?.vehicleInsurances?.length > 0) {
        vehicleDetails.vehicleInsurances.map((ins) => {
          if (ins.status === "active") {
            latestInsurance.push(ins)
          }
        })
      }

      // Removed BACK_URL concatenation from registration_document
      // (Now handled after load)
      if (vehicleDetails) {
        delete vehicleDetails.vehicleInsurances
      }

      let cancelledTripsCount = 0
      let totalTripsAssignCount = 0
      const counts: any = await this.getCancelledTripsCount(driverId)
      if (counts && counts?.data) {
        cancelledTripsCount = counts.data.cancelledTrips
        totalTripsAssignCount = counts.data.assignedTrips
      }

      const completedTripsCount = await this.tripRepository["entity"]
        .createQueryBuilder("trip")
        .innerJoin(
          TripFleetAssignment,
          "assignment",
          "assignment.trip_id = trip.id",
        )
        .where("assignment.driver_id = :driverId", { driverId })
        .andWhere("trip.status = :status", { status: STATUS.COMPLETED })
        .getCount()

      let rating = {}
      const ratingData: any = await this.getRating(driverId)
      if (ratingData && ratingData?.data) {
        rating = ratingData.data
      }

      const {
        id,
        first_name,
        last_name,
        email,
        phone_number,
        profile_photo,
        country_code,
        duty_status,
        role,
        reporting_to,
        trip_driver,
        languages,
      } = driver

      const profileWithVehicle = {
        id,
        first_name,
        last_name,
        email,
        phone_number,
        profile_photo,
        country_code,
        duty_status,
        role,
        reporting_to: reporting_to
          ? {
              ...reporting_to,
              profile_photo:
                reporting_to.profile_photo &&
                reporting_to.profile_photo !== "null" &&
                reporting_to.profile_photo !== "undefined"
                  ? reporting_to.profile_photo
                  : null,
            }
          : null,
        trip_driver,
        vehicle: vehicleDetails,
        driving_license,
        registration: registration_doc,
        insurance: latestInsurance,
        cancelledTripsCount,
        totalTripsAssignCount,
        completedTripsCount,
        rating,
        ongoing: ongoingTrip?.tfa_trip_id || null,
        contact: CONTACT,
        languages,
      }

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, { ":data": "Driver Profile" }),
        profileWithVehicle,
      )
    } catch (error) {
      console.error("Error in getDriverProfileDeatails:", error)
      return failureResponse(500, messageKey.something_went_wrong)
    }
  }

  async getDispatcherProfileDetails(dispatcherId: number) {
    try {
      const dispatcher: TeamMember =
        (await this.teamMemberRepository.getByParams({
          where: { id: dispatcherId },
          relations: [
            "role:id,name",
            "reporting_to:id,first_name,last_name,country_code,phone_number,profile_photo",
            "id_proofs:id,document_type,document_number,expiry_date,document_files",
            "languages:id,code,name",
          ],
          select: [
            "id",
            "first_name",
            "last_name",
            "email",
            "phone_number",
            "profile_photo",
            "country_code",
            "duty_status",
            "dob",
            "gender",
            "status",
            "current_step",
          ],
          findOne: true,
        })) as any

      if (!dispatcher) {
        return failureResponse(
          code.DATA_NOT_FOUND,
          errorMessage(messageKey.data_not_found, { ":data": "Dispatcher" }),
        )
      }

      // Get total trips for customers managed by this dispatcher
      const totalTripsCount = await this.tripRepository["entity"]
        .createQueryBuilder("trip")
        .innerJoin("trip.customer", "customer")
        .where("customer.dispatcher_id = :dispatcherId", { dispatcherId })
        .andWhere("trip.deleted_at IS NULL")
        .getCount()

      // Get total customers managed by this dispatcher

      const customersCount = await this.customerRepository["entity"]
        .createQueryBuilder("customer")
        .where("customer.dispatcher_id = :dispatcherId", { dispatcherId })
        .andWhere("customer.deleted_at IS NULL")
        .getCount()

      const customerInTripTodayCount = await this.tripRepository["entity"]
        .createQueryBuilder("trip")
        .innerJoin("trip.customer", "customer")
        .where("customer.dispatcher_id = :dispatcherId", { dispatcherId })
        .andWhere("customer.deleted_at IS NULL")
        .andWhere("trip.deleted_at IS NULL")
        .andWhere("DATE(trip.pickup_datetime) = :pickupDate", {
          pickupDate: moment(new Date()).format("YYYY-MM-DD"),
        })
        .select("customer.id")
        .groupBy("customer.id")
        .getCount()

      // Get total drivers reporting to this dispatcher
      const driversCount = await this.teamMemberEntity
        .createQueryBuilder("driver")
        .innerJoin("driver.role", "role")
        .where("driver.reporting_to_id = :dispatcherId", { dispatcherId })
        .andWhere("LOWER(role.name) = :roleName", { roleName: "driver" })
        .andWhere("driver.deleted_at IS NULL")
        .getCount()

      const driverInTripTodayCount = await this.tripRepository["entity"]
        .createQueryBuilder("trip")
        .innerJoin("trip.assignments", "assignments")
        .innerJoin("assignments.driver", "driver")
        .where("driver.reporting_to_id = :dispatcherId", { dispatcherId })
        .andWhere("driver.deleted_at IS NULL")
        .andWhere("trip.deleted_at IS NULL")
        .andWhere("DATE(trip.pickup_datetime) = :pickupDate", {
          pickupDate: moment(new Date()).format("YYYY-MM-DD"),
        })
        .select("driver.id")
        .groupBy("driver.id")
        .getCount()

      if (dispatcher.id_proofs && dispatcher.id_proofs.length > 0) {
        dispatcher.id_proofs = dispatcher.id_proofs.map((proof) => {
          if (proof.document_files) {
            proof.document_files = proof.document_files
          }
          return proof
        })
      }

      const {
        id,
        first_name,
        last_name,
        email,
        phone_number,
        profile_photo,
        country_code,
        duty_status,
        role,
        reporting_to,
        languages,
        dob,
        gender,
        status,
        current_step,
      } = dispatcher

      const dispatcherProfile = {
        id,
        first_name,
        last_name,
        email,
        phone_number,
        dob,
        gender,
        status,
        current_step,
        profile_photo,
        country_code,
        duty_status,
        role,
        reporting_to: reporting_to,
        totalTripsCount,
        customersCount,
        customerEnRought: Number(customerInTripTodayCount || 0),
        driversCount,
        driverOccupied: Number(driverInTripTodayCount || 0),
        contact: CONTACT,
        languages,
      }

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, {
          ":data": "Dispatcher Profile",
        }),
        dispatcherProfile,
      )
    } catch (error) {
      console.error("Error in getDispatcherProfileDetails:", error)
      return failureResponse(500, messageKey.something_went_wrong)
    }
  }

  async addDriverLicenceForDriver(
    teamMemberId: number,
    documentMetadata: {
      document_number: string
      expiry_date?: string
      document_status?: string
    },
    documentFile?: Express.Multer.File,
  ) {
    const teamMember = (await this.teamMemberRepository.getByParams({
      where: { id: teamMemberId },
      findOne: true,
      relations: ["id_proofs"],
    })) as any

    if (!teamMember || Array.isArray(teamMember)) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.data_not_found, {
          ":data": "Driver",
        }),
      )
    }

    const documentType = "driving-license"
    let documentPath: string | null = null

    if (documentFile?.path) {
      const fs = await import("fs/promises")
      const path = await import("path")

      const ext = documentFile.originalname.split(".").pop()
      const timestamp = Date.now()
      const sanitizedType = documentType
        .replace(/[^a-z0-9]/gi, "-")
        .toLowerCase()

      const newFileName = `${sanitizedType}-${timestamp}.${ext}`
      const newRelativePath = `uploads/team-members-id-proofs/${newFileName}`
      const newAbsolutePath = path.resolve("public", newRelativePath)

      await fs.rename(documentFile.path, newAbsolutePath)
      documentPath = newRelativePath.replace(/\\/g, "/")
    }

    const existingLicence = teamMember.id_proofs.find(
      (proof) => proof.document_type === documentType,
    )

    if (existingLicence) {
      existingLicence.document_number = documentMetadata.document_number
      existingLicence.expiry_date = documentMetadata.expiry_date || null
      existingLicence.document_status = documentMetadata.document_status || null
      if (documentPath) {
        existingLicence.document_files = documentPath
      }

      await this.idProofRepository.save(existingLicence)
    } else {
      const newProof = {
        document_type: documentType,
        document_number: documentMetadata.document_number,
        expiry_date: documentMetadata.expiry_date || null,
        document_status: documentMetadata.document_status || null,
        document_files: documentPath,
        team_member_id: teamMemberId,
      }

      await this.idProofRepository.save(newProof)
    }

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_update, {
        ":data": "Driving licence",
      }),
    )
  }

  /**
   * Fetches driver's assigned trips and builds the time-overlap exclusion condition
   * for requested-trip queries (so requested trips don't overlap assigned ones).
   * @param futureOnly - if true, only assigned trips with pickup_datetime >= today
   */
  private async getAssignedTripsOverlapCondition(
    driverId: number,
    futureOnly: boolean,
  ): Promise<{
    params: Record<string, unknown>
    excludeTimeCondition: string
  }> {
    const qb = this.tripFleetAssignmentRepository
      .createQueryBuilder("tfa")
      .innerJoinAndSelect("tfa.trip", "t")
      .leftJoin("trip_cancellations", "tc", "tc.trip_id = t.id")
      .where("tfa.driver_id = :driverId", { driverId })
      .andWhere("tfa.released_at IS NULL")
      .andWhere("tc.id IS NULL")
      .andWhere("tfa.trip_completed_at IS NULL")
      .andWhere("t.deleted_at IS NULL")
    if (futureOnly) {
      qb.andWhere("DATE(t.pickup_datetime) >= DATE(NOW())")
    }
    const assignedTrips = await qb
      .select([
        "tfa.id AS fleet_assignment_id",
        "t.pickup_datetime AS pickup_datetime",
        "t.trip_timezone AS trip_timezone",
        "t.dropoff_datetime AS dropoff_datetime",
      ])
      .getRawMany()

    const params: Record<string, unknown> = { driverId }
    let excludeTimeCondition = ""
    assignedTrips.forEach((tripFleet: any, index: number) => {
      const startParam = `start${index}`
      const endParam = `end${index}`
      const tripEnd = tripFleet.dropoff_datetime
      params[startParam] = formateDate(tripFleet.pickup_datetime)
      if (tripEnd) {
        params[endParam] = formateDate(tripEnd)
        excludeTimeCondition += ` AND NOT (
            ( t.pickup_datetime < :${endParam} AND t.dropoff_datetime > :${startParam} )
          )`
      }
    })
    return { params, excludeTimeCondition }
  }

  async getDriverTrips(
    driverId: number,
    type?: "requests" | "upcoming" | "past",
    pagination?: { page: number; limit: number },
    pastTripType?: "completed" | "cancelled" | "all",
    fromDate?: string,
  ) {
    const page = pagination?.page || 1
    const limit = pagination?.limit || 10
    const skip = (page - 1) * limit

    const tripType = type || "requests"

    const addHospitalJoins = (qb: any) => {
      return qb
        .leftJoin(
          "hospitals",
          "hp",
          "hp.id = t.pickup_hospital_id AND t.pickup_location_type = 'hospital'",
        )
        .leftJoin("cities", "cp", "cp.id = hp.city_id")
        .leftJoin("states", "sp", "sp.id = hp.state_id")
        .leftJoin(
          "hospitals",
          "hd",
          "hd.id = t.dropoff_hospital_id AND t.dropoff_location_type = 'hospital'",
        )
        .leftJoin("cities", "cd", "cd.id = hd.city_id")
        .leftJoin("states", "sd", "sd.id = hd.state_id")
    }

    const addSelectFields = (qb: any) => {
      return qb.select([
        "t.id AS id",
        "t.pickup_datetime AS pickup_datetime",
        "t.trip_timezone AS trip_timezone",
        "t.status AS trip_status",
        `case
          when t.pickup_location_type = 'hospital'
          	and hp.address is not null then
            CONCAT_WS(', ', hp.address, cp.name, sp.name)
          when t.pickup_location_type = 'residence' then
            c.primary_address
          else
            t.pick_up_location
          end as pick_up_address`,
        `case
          when t.dropoff_location_type = 'hospital'
            and hd.address is not null then
            CONCAT_WS(', ', hd.address, cd.name, sd.name)
          when t.dropoff_location_type = 'residence' then
            c.primary_address
        	else
            t.drop_off_location
          end as drop_off_address`,
        "t.pickup_location_lan AS pickup_location_lan",
        "t.pickup_location_long AS pickup_location_long",
        "t.dropoff_location_lan AS dropoff_location_lan",
        "t.dropoff_location_long AS dropoff_location_long",
        "t.pickup_location_type AS pickup_location_type",
        "t.dropoff_location_type AS dropoff_location_type",
        "hd.latitude AS dropoff_hospital_latitude",
        "hd.longitude AS dropoff_hospital_longitude",
        "hp.latitude AS pickup_hospital_latitude",
        "hp.longitude AS pickup_hospital_longitude",
        "c.customer_name AS customer_name",
        "c.primary_address AS customer_primary_address",
        "c.latitude AS customer_latitude",
        "c.longitude AS customer_longitude",
        `COALESCE(ARRAY_AGG(DISTINCT "ao"."name" ORDER BY "ao"."name") FILTER (WHERE "ao"."name" IS NOT NULL), '{}') AS addon_names`,
        `COALESCE(
          (
            SELECT ARRAY_AGG(direction ORDER BY id)
            FROM (
              SELECT DISTINCT ON (tbs2.id) tbs2.id, tbs2.direction
              FROM trip_baby_seats tbs2
              WHERE tbs2.trip_id = t.id
              ORDER BY tbs2.id
            ) uniq
          ),
          '{}'
        ) AS baby_seat_directions
        `,
        "COALESCE(fleet.registration_number, '') AS registration_number",
      ])
    }

    if (tripType === "requests") {
      const assignedTrips = await this.tripFleetAssignmentRepository
        .createQueryBuilder("tfa")
        .innerJoinAndSelect("tfa.trip", "t")
        .leftJoin("trip_cancellations", "tc", "tc.trip_id = t.id")
        .where("tfa.driver_id = :driverId", { driverId })
        .andWhere("tfa.released_at IS NULL")
        .andWhere("tc.id IS NULL")
        .andWhere("tfa.trip_completed_at IS NULL")
        .andWhere("t.deleted_at IS NULL")
        .andWhere("DATE(t.pickup_datetime) >= DATE(NOW())")
        .select([
          "tfa.id AS fleet_assignment_id",
          "t.pickup_datetime AS pickup_datetime",
          "t.trip_timezone AS trip_timezone",
          "t.dropoff_datetime AS dropoff_datetime",
        ])
        .getRawMany()

      const params: any = { driverId }
      let excludeTimeCondition = ""

      assignedTrips.forEach((tripFleet, index) => {
        const startParam = `start${index}`
        const endParam = `end${index}`

        const tripStart = tripFleet.pickup_datetime
        const tripEnd = tripFleet.dropoff_datetime

        params[startParam] = formateDate(tripStart)
        if (tripEnd) {
          params[endParam] = formateDate(tripEnd)
        }

        excludeTimeCondition += ` AND NOT (
            (
              t.pickup_datetime < :${endParam}
            AND
              t.dropoff_datetime > :${startParam}
            )
          )`
      })

      const queryBuilder =
        this.tripFleetAssignmentRepository.createQueryBuilder("tfa")

      addSelectFields(queryBuilder)

      queryBuilder
        .innerJoin("trips", "t", "t.id = tfa.trip_id")
        .innerJoin("customers", "c", "c.id = t.customer_id")
        .innerJoin(
          "fleet_operations",
          "fleet",
          "fleet.vehicle_type_id = tfa.fleet_type_id",
        )
        .innerJoin(
          "team_members",
          "tm",
          "tm.reporting_to_id = c.dispatcher_id AND tm.id = :driverId",
          { driverId },
        )
        .leftJoin("trip_cancellations", "tc", "tc.trip_id = t.id")
        .leftJoin("vehicle_maintenances", "vm", "vm.fleet_id = tfa.fleet_id")
        .leftJoin("trip_addons", "ta", "t.id = ta.trip_id")
        .leftJoin("add_ons", "ao", "ao.id = ta.addon_id")
        .leftJoin("trip_baby_seats", "tbs", "tbs.trip_id = t.id")
        .leftJoin(
          "declined_trips",
          "dt",
          "dt.trip_id = t.id AND dt.driver_id = :driverId",
          { driverId },
        )
      addHospitalJoins(queryBuilder)
      queryBuilder
        .where("fleet.assigned_driver = :driverId", { driverId })
        .andWhere("DATE(t.pickup_datetime) >= DATE(NOW())")
        .andWhere(
          "(vm.next_scheduled_maintenance IS NULL OR DATE(vm.next_scheduled_maintenance) != DATE(t.pickup_datetime))",
        )
        .andWhere("t.is_trip_open = true")
        .andWhere("tfa.driver_id IS NULL")
        .andWhere("t.deleted_at IS NULL")
        .andWhere("dt.id IS NULL")
        .andWhere("tc.id IS NULL")
        .andWhere("t.status != :invoicedStatus", {
          invoicedStatus: STATUS.INVOICED,
        })
        .andWhere(`1=1 ${excludeTimeCondition}`, params)
        .addSelect(
          `CASE WHEN t.pickup_datetime::date = CURRENT_DATE THEN 1 WHEN t.pickup_datetime::date > CURRENT_DATE THEN 2 ELSE 3 END`,
          "date_priority",
        )
        .groupBy("t.id")
        .addGroupBy("c.id")
        .addGroupBy("hp.id")
        .addGroupBy("cp.id")
        .addGroupBy("sp.id")
        .addGroupBy("hd.id")
        .addGroupBy("cd.id")
        .addGroupBy("sd.id")
        .addGroupBy("t.status")
        .addGroupBy("fleet.id")
        .orderBy("date_priority", "ASC")
        .addOrderBy("t.pickup_datetime", "ASC")

      const [data, count] = await Promise.all([
        queryBuilder.offset(skip).limit(limit).getRawMany(),
        queryBuilder.getCount(),
      ])

      return successResponse(code.SUCCESS, messageKey.data_retrieve, {
        data,
        count,
      })
    }

    if (tripType === "upcoming") {
      const queryBuilder =
        this.tripFleetAssignmentRepository.createQueryBuilder("tfa")

      addSelectFields(queryBuilder)

      queryBuilder
        .innerJoin("tfa.trip", "t")
        .innerJoin("t.customer", "c")
        .innerJoin("tfa.fleet", "fleet")
        .leftJoin("trip_cancellations", "tc", "tc.trip_id = t.id")
        .leftJoin("trip_addons", "ta", "t.id = ta.trip_id")
        .leftJoin("add_ons", "ao", "ao.id = ta.addon_id")
        .leftJoin("trip_baby_seats", "tbs", "tbs.trip_id = t.id")

      addHospitalJoins(queryBuilder)

      // Base filters
      queryBuilder
        .where("tfa.driver_id = :driverId", { driverId })
        .andWhere("t.deleted_at IS NULL")
        .andWhere("t.status != 'completed'")
        .andWhere("t.status != 'cancelled'")
        .andWhere("t.status != :invoicedStatus", {
          invoicedStatus: STATUS.INVOICED,
        })
        .andWhere("tc.id IS NULL")

      // 🔥 Ignore past trips, except pickup/ongoing
      queryBuilder.andWhere(
        "(t.status IN ('pickup','ongoing') OR DATE(t.pickup_datetime) >= CURRENT_DATE)",
      )

      // Optional fromDate filter
      if (fromDate) {
        queryBuilder.andWhere("DATE(t.pickup_datetime) >= DATE(:fromDate)", {
          fromDate,
        })
      }

      // 🔥 Priority sorting logic
      queryBuilder
        .addSelect(
          `
      CASE
        WHEN t.status IN ('pickup','ongoing') THEN 1
        WHEN DATE(t.pickup_datetime) = CURRENT_DATE THEN 2
        WHEN DATE(t.pickup_datetime) > CURRENT_DATE THEN 3
        ELSE 4
      END
      `,
          "priority",
        )
        .groupBy("t.id")
        .addGroupBy("c.id")
        .addGroupBy("hp.id")
        .addGroupBy("cp.id")
        .addGroupBy("sp.id")
        .addGroupBy("hd.id")
        .addGroupBy("cd.id")
        .addGroupBy("sd.id")
        .addGroupBy("t.status")
        .addGroupBy("fleet.id")
        .orderBy("priority", "ASC")
        .addOrderBy("t.pickup_datetime", "ASC")

      const [data, count] = await Promise.all([
        queryBuilder.offset(skip).limit(limit).getRawMany(),
        queryBuilder.getCount(),
      ])

      return successResponse(code.SUCCESS, messageKey.data_retrieve, {
        data,
        count,
      })
    }

    if (tripType === "past") {
      // 1) Past assigned: completed, cancelled, or scheduled-with-past-pickup where driver was assigned
      const buildPastAssignedQuery = () => {
        const qb = this.tripFleetAssignmentRepository.createQueryBuilder("tfa")
        addSelectFields(qb)
        qb.innerJoin("tfa.trip", "t")
          .innerJoin("t.customer", "c")
          .leftJoin("tfa.fleet", "fleet")
          .leftJoin("trip_addons", "ta", "t.id = ta.trip_id")
          .leftJoin("add_ons", "ao", "ao.id = ta.addon_id")
          .leftJoin("trip_baby_seats", "tbs", "tbs.trip_id = t.id")
        addHospitalJoins(qb)
        qb.where("tfa.driver_id = :driverId", { driverId })
          .andWhere(
            "(t.status IN (:...assignedStatuses) OR (t.status = :scheduledStatus AND DATE(t.pickup_datetime) < CURRENT_DATE))",
            {
              assignedStatuses: [STATUS.CANCELLED, STATUS.COMPLETED],
              scheduledStatus: STATUS.SCHEDULED,
            },
          )
          .andWhere("t.deleted_at IS NULL")
          .andWhere("t.status != :invoicedStatus", {
            invoicedStatus: STATUS.INVOICED,
          })
          .groupBy("t.id")
          .addGroupBy("c.id")
          .addGroupBy("hp.id")
          .addGroupBy("cp.id")
          .addGroupBy("sp.id")
          .addGroupBy("hd.id")
          .addGroupBy("cd.id")
          .addGroupBy("sd.id")
          .addGroupBy("t.status")
          .addGroupBy("fleet.id")
        if (pastTripType === "completed") {
          qb.andWhere("t.status = :status", { status: STATUS.COMPLETED })
        } else if (pastTripType === "cancelled") {
          qb.leftJoin(
            "declined_trips",
            "dt",
            "dt.trip_id = t.id AND dt.driver_id = :driverId",
            { driverId },
          )
            .leftJoin("trip_cancellations", "tc", "tc.trip_id = t.id")
            .andWhere("(dt.id IS NOT NULL OR tc.id IS NOT NULL)")
        }
        return qb.orderBy("t.pickup_datetime", "DESC")
      }

      // 2) Past requested: status = requested, trip start date has passed, driver reports to
      //    the trip customer's dispatcher, and driver has same vehicle type as the requested trip
      const buildPastRequestedQuery = () => {
        const qb = this.tripFleetAssignmentRepository.createQueryBuilder("tfa")
        addSelectFields(qb)
        qb.innerJoin("trips", "t", "t.id = tfa.trip_id")
          .innerJoin("customers", "c", "c.id = t.customer_id")
          .innerJoin(
            "fleet_operations",
            "fleet",
            "fleet.vehicle_type_id = tfa.fleet_type_id",
          )
          .innerJoin(
            "team_members",
            "tm",
            "tm.reporting_to_id = c.dispatcher_id AND tm.id = :driverId",
            { driverId },
          )
          .leftJoin("trip_cancellations", "tc", "tc.trip_id = t.id")
          .leftJoin("trip_addons", "ta", "t.id = ta.trip_id")
          .leftJoin("add_ons", "ao", "ao.id = ta.addon_id")
          .leftJoin("trip_baby_seats", "tbs", "tbs.trip_id = t.id")
        addHospitalJoins(qb)
        qb.where("fleet.assigned_driver = :driverId", { driverId })
          .andWhere("DATE(t.pickup_datetime) < CURRENT_DATE")
          .andWhere("tfa.driver_id IS NULL")
          .andWhere("t.deleted_at IS NULL")
          .andWhere("tc.id IS NULL")
          .andWhere("t.status IN (:...requestedStatuses)", {
            requestedStatuses: [STATUS.REQUESTED],
          })
          .andWhere("t.status != :invoicedStatus", {
            invoicedStatus: STATUS.INVOICED,
          })
          .groupBy("t.id")
          .addGroupBy("c.id")
          .addGroupBy("hp.id")
          .addGroupBy("cp.id")
          .addGroupBy("sp.id")
          .addGroupBy("hd.id")
          .addGroupBy("cd.id")
          .addGroupBy("sd.id")
          .addGroupBy("t.status")
          .addGroupBy("fleet.id")
        return qb.orderBy("t.pickup_datetime", "DESC")
      }

      const includeRequested =
        pastTripType !== "completed" && pastTripType !== "cancelled"

      if (!includeRequested) {
        // Completed or cancelled only: return assigned trips only (no past requested)
        const pastAssignedQbForCount = buildPastAssignedQuery()
        const pastAssignedQbForData = buildPastAssignedQuery()
        const [data, count] = await Promise.all([
          pastAssignedQbForData.offset(skip).limit(limit).getRawMany(),
          pastAssignedQbForCount.getCount(),
        ])
        return successResponse(code.SUCCESS, messageKey.data_retrieve, {
          data,
          count,
          direction: "DESC",
        })
      }

      // All: assigned (completed/cancelled) + past requested. We run two queries and merge in app
      // because the two result sets have different join shapes; a single UNION would require
      // raw SQL and duplicate select/param handling. Pagination: fetch enough from each stream
      // so the merged, sorted slice [skip, skip+limit) is correct.
      const pastAssignedQbForCount = buildPastAssignedQuery()
      const pastAssignedQbForData = buildPastAssignedQuery()
      const pastRequestedQbForCount = buildPastRequestedQuery()
      const pastRequestedQbForData = buildPastRequestedQuery()

      const needed = skip + limit
      const [countAssigned, countRequested, dataAssigned, dataRequested] =
        await Promise.all([
          pastAssignedQbForCount.getCount(),
          pastRequestedQbForCount.getCount(),
          pastAssignedQbForData.offset(0).limit(needed).getRawMany(),
          pastRequestedQbForData.offset(0).limit(needed).getRawMany(),
        ])

      type PastTripRow = { pickup_datetime: string | Date }
      const byPickupDesc = (a: PastTripRow, b: PastTripRow) =>
        new Date(b.pickup_datetime).getTime() -
        new Date(a.pickup_datetime).getTime()
      const data = [...dataAssigned, ...dataRequested]
        .sort(byPickupDesc)
        .slice(skip, skip + limit)
      const count = countAssigned + countRequested

      return successResponse(code.SUCCESS, messageKey.data_retrieve, {
        data,
        count,
        direction: "DESC",
      })
    }

    return failureResponse(code.ERROR, "Invalid trip type specified")
  }

  async getDispatcherTrips(
    dispatcherId: number,
    tripType?: "ongoing" | "upcoming" | "requested" | "past" | "active",
    limit = 10,
    skip = 0,
    pastTripType?: "completed" | "cancelled" | "all",
    sortKey?: string,
    sortOrder: "ASC" | "DESC" = "ASC",
  ) {
    const offset = skip

    // Define valid sort columns for trips
    const validSortColumns = [
      "pickup_datetime",
      "created_at",
      "updated_at",
      "status",
      "trip_timezone",
    ]

    // Validate and set sort column
    const sortColumn =
      sortKey && validSortColumns.includes(sortKey)
        ? sortKey
        : "pickup_datetime"
    const addHospitalJoins = (qb: any) => {
      return qb
        .leftJoin(
          "hospitals",
          "hp",
          "hp.id = t.pickup_hospital_id AND t.pickup_location_type = 'hospital'",
        )
        .leftJoin("cities", "cp", "cp.id = hp.city_id")
        .leftJoin("states", "sp", "sp.id = hp.state_id")
        .leftJoin(
          "hospitals",
          "hd",
          "hd.id = t.dropoff_hospital_id AND t.dropoff_location_type = 'hospital'",
        )
        .leftJoin("cities", "cd", "cd.id = hd.city_id")
        .leftJoin("states", "sd", "sd.id = hd.state_id")
    }

    const addSelectFields = (qb: any, isRequested: boolean) => {
      const selectFields = [
        "t.id AS id",
        "t.pickup_datetime AS pickup_datetime",
        "t.trip_timezone AS trip_timezone",
        "t.status AS trip_status",
        `case
          when t.pickup_location_type = 'hospital'
            and hp.address is not null then
          CONCAT_WS(', ', hp.address, cp.name, sp.name)
        when t.pickup_location_type = 'residence' then
          c.primary_address
          else
          t.pick_up_location
          end as pick_up_address`,
        `case
            when t.dropoff_location_type = 'hospital'
              and hd.address is not null then
          CONCAT_WS(', ', hd.address, cd.name, sd.name)
        when t.dropoff_location_type = 'residence' then
          c.primary_address
          else
          t.drop_off_location
          end as drop_off_address`,
        "t.pickup_location_lan AS pickup_location_lan",
        "t.pickup_location_long AS pickup_location_long",
        "t.dropoff_location_lan AS dropoff_location_lan",
        "t.dropoff_location_long AS dropoff_location_long",
        "t.pickup_location_type AS pickup_location_type",
        "t.dropoff_location_type AS dropoff_location_type",
        "hd.latitude AS dropoff_hospital_latitude",
        "hd.longitude AS dropoff_hospital_longitude",
        "hp.latitude AS pickup_hospital_latitude",
        "hp.longitude AS pickup_hospital_longitude",
        "c.customer_name AS customer_name",
        "c.latitude AS customer_latitude",
        "c.longitude AS customer_longitude",
        "c.primary_address AS customer_primary_address",
        `COALESCE(ARRAY_AGG(DISTINCT "ao"."name" ORDER BY "ao"."name") FILTER (WHERE "ao"."name" IS NOT NULL), '{}') AS addon_names`,
        `COALESCE(
          (
            SELECT ARRAY_AGG(direction ORDER BY id)
            FROM (
              SELECT DISTINCT ON (tbs2.id) tbs2.id, tbs2.direction
              FROM trip_baby_seats tbs2
              WHERE tbs2.trip_id = t.id
              ORDER BY tbs2.id
            ) uniq
          ),
          '{}'
        ) AS baby_seat_directions`,
      ]

      if (isRequested) {
        selectFields.push("'' AS registration_number")
        selectFields.push("'' AS driver_name")
        selectFields.push("'' AS driver_phone")
      } else {
        selectFields.push(
          "COALESCE(fleet.registration_number, '') AS registration_number",
        )
        selectFields.push(
          "COALESCE(tm.first_name || ' ' || tm.last_name, '') AS driver_name",
        )
        selectFields.push("tm.phone_number AS driver_phone")
      }
      return qb.select(selectFields)
    }

    let commonGroupByFields = [
      "t.id",
      "c.id",
      "hp.id",
      "cp.id",
      "sp.id",
      "hd.id",
      "cd.id",
      "sd.id",
    ]

    const queryBuilder = this.tripEntityRepository.createQueryBuilder("t")
    addSelectFields(queryBuilder, false)

    queryBuilder
      .leftJoin("t.assignments", "tfa")
      .leftJoin("t.customer", "c")
      .leftJoin("tfa.fleet", "fleet")
      .leftJoin("tfa.driver", "tm")
      .leftJoin("trip_addons", "ta", "t.id = ta.trip_id")
      .leftJoin("add_ons", "ao", "ao.id = ta.addon_id")
      .where("c.dispatcher_id = :dispatcherId", { dispatcherId })
      .andWhere("t.deleted_at IS NULL")
      .andWhere("t.status != :invoicedStatus", {
        invoicedStatus: STATUS.INVOICED,
      })

    addHospitalJoins(queryBuilder)

    commonGroupByFields = [...commonGroupByFields, "fleet.id", "tm.id"]
    queryBuilder.groupBy(commonGroupByFields.join(", "))

    switch (tripType) {
      case "requested":
        // Request tab: Current to Future only (no past). Order: current date first, then future chronological.
        queryBuilder
          .andWhere("t.status IN (:...statuses)", {
            statuses: [STATUS.REQUESTED, STATUS.REQUESTED_BY_CUSTOMER],
          })
          .andWhere("DATE(t.pickup_datetime) >= CURRENT_DATE")
          .addSelect(
            `CASE WHEN t.pickup_datetime::date = CURRENT_DATE THEN 1 WHEN t.pickup_datetime::date > CURRENT_DATE THEN 2 ELSE 3 END`,
            "date_priority",
          )
          .orderBy("date_priority", "ASC")
          .addOrderBy("t.pickup_datetime", "ASC")
        break
      case "ongoing":
        // Ongoing tab: Past → Current → Future. Order: past first, then current, then future.
        queryBuilder.andWhere("t.status IN (:...status)", {
          status: [STATUS.ONGOING, STATUS.PICKUP],
        })
        queryBuilder
          .addSelect(
            `CASE WHEN t.pickup_datetime::date < CURRENT_DATE THEN 1 WHEN t.pickup_datetime::date = CURRENT_DATE THEN 2 ELSE 3 END`,
            "date_priority",
          )
          .orderBy("date_priority", "ASC")
          .addOrderBy("t.pickup_datetime", "ASC")
        break
      case "upcoming":
        // Schedule tab: Current to Future only (no past). Order: current then future chronological.
        queryBuilder
          .andWhere("t.status IN (:...statuses)", {
            statuses: [STATUS.UPCOMING, STATUS.SCHEDULED],
          })
          .andWhere("DATE(t.pickup_datetime) >= CURRENT_DATE")
          .orderBy("t.pickup_datetime", "ASC")
        break
      case "active":
        queryBuilder
          .andWhere("t.status = :status", {
            status: STATUS.ACTIVE,
          })
          .orderBy(`t.${sortColumn}`, sortOrder)
        break
      case "past":
        // History (All / Completed / Cancelled): Newest month first, within month newest date first.
        if (pastTripType === "completed") {
          queryBuilder.andWhere("t.status = :status", {
            status: STATUS.COMPLETED,
          })
        } else if (pastTripType === "cancelled") {
          queryBuilder.andWhere("t.status = :status", {
            status: STATUS.CANCELLED,
          })
        } else {
          queryBuilder.andWhere(
            "(t.status IN (:...statuses) OR DATE(t.pickup_datetime) < CURRENT_DATE)",
            { statuses: [STATUS.CANCELLED, STATUS.COMPLETED] },
          )
        }
        queryBuilder
          .orderBy("DATE_TRUNC('month', t.pickup_datetime)", "DESC")
          .addOrderBy("t.pickup_datetime", "DESC")
        break
      default:
        queryBuilder.orderBy(`t.${sortColumn}`, sortOrder)
        break
    }

    // Get data with pagination and calculate accurate count separately
    const data = await queryBuilder.offset(offset).limit(limit).getRawMany()
    const count = await queryBuilder.getCount()

    return successResponse(code.SUCCESS, messageKey.data_retrieve, {
      data,
      count,
      skip,
      limit,
    })
  }

  async declineTrip(tripId: number, driverId: number) {
    const existingDeclinedTrip = await this.declinedTripRepository.getByParams({
      where: { trip_id: tripId, driver_id: driverId },
      findOne: true,
    })

    if (existingDeclinedTrip) {
      return failureResponse(code.VALIDATION, "Trip has already been declined")
    }

    await this.declinedTripRepository.save({
      trip_id: tripId,
      driver_id: driverId,
    })

    // Find userId from driverId
    const user = (await this.authRepository.getByParams({
      where: { team_member_id: driverId },
      findOne: true,
      select: ["id"],
    })) as Auth

    const userId = user?.id

    if (userId) {
      await this.notificationService.updateNotificationByUserIdAndTripId(
        userId,
        tripId,
      )
    }

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_update, {
        ":data": "Trip declined successfully",
      }),
    )
  }

  async acceptTrip(tripId: number, driverId: number, fleetId: number) {
    const driver = (await this.teamMemberRepository.getByParams({
      where: {
        id: driverId,
      },
    })) as TeamMember

    const existingAssignment = await this.tripFleetAssignmentRepository.findOne(
      {
        where: {
          trip_id: tripId,
          driver_id: IsNull(),
        },
        relations: [
          "trip",
          "trip.customer",
          "trip.customer.dispatcher",
          "trip.customer.dispatcher.users",
          "driver",
        ],
      },
    )

    if (!existingAssignment || existingAssignment?.driver_id) {
      return failureResponse(
        code.VALIDATION,
        "Trip is already assigned to another driver",
      )
    }

    // Validation: Check if driver has other trips in the same time slot
    const newTrip = existingAssignment.trip
    const overlappingTrips = await this.tripFleetAssignmentRepository
      .createQueryBuilder("tfa")
      .innerJoin("tfa.trip", "t")
      .leftJoin("trip_cancellations", "tc", "tc.trip_id = t.id")
      .where("tfa.driver_id = :driverId", { driverId })
      .andWhere("t.deleted_at IS NULL")
      .andWhere("tc.id IS NULL")
      .andWhere(
        "(t.status IN (:...statuses) OR (tfa.trip_completed_at IS NULL))",
        {
          statuses: [
            STATUS.ONGOING,
            STATUS.SCHEDULED,
            STATUS.PICKUP,
            STATUS.UPCOMING,
          ],
        },
      )
      .andWhere(
        "(t.pickup_datetime < :newDropoffDatetime AND t.dropoff_datetime > :newPickupDatetime)",
        {
          newPickupDatetime: newTrip.pickup_datetime,
          newDropoffDatetime: newTrip.dropoff_datetime,
        },
      )
      .getCount()

    if (overlappingTrips > 0) {
      return failureResponse(
        code.VALIDATION,
        "Driver has another trip assigned for the same time slot. Cannot accept this trip.",
      )
    }

    await this.tripFleetAssignmentRepository.update(
      {
        trip_id: tripId,
      },
      {
        driver_id: driverId,
        fleet_id: fleetId,
      },
    )

    await this.tripRepository.save(
      {
        status: STATUS.SCHEDULED,
        is_trip_open: false,
      },
      { id: tripId },
    )

    // Fetch driver details after assignment
    const assignedDriver = (await this.teamMemberRepository.getByParams({
      where: { id: driverId },
      findOne: true,
    })) as TeamMember

    const admins = await this.authService.getAdmins()
    const adminIds = admins.map((admin) => admin.id)
    const dispatcherIds =
      existingAssignment?.trip?.customer?.dispatcher?.users[0]?.id
    if (dispatcherIds) {
      adminIds.push(dispatcherIds)
    }
    if (adminIds.length > 0) {
      this.notificationService.sendNotification({
        user_ids: adminIds,
        title: "New trip accepted by driver",
        message: formatMessage("tripAcceptedByDriver", {
          CUSTOMER: existingAssignment?.trip?.customer?.customer_name,
          DRIVER: `${assignedDriver?.first_name} ${assignedDriver?.last_name}`,
          TRIP_ID: existingAssignment?.trip_id?.toString(),
        }),
        data: {
          type: "trip",
          trip_id: existingAssignment?.trip_id,
          trip_status: STATUS.SCHEDULED,
        },
      })
    }

    if (driver && driver.user_id) {
      await this.notificationService.updateNotificationByTripIdExcludingUser(
        driver.user_id,
        tripId,
      )
    }

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_update, {
        ":data": "Trip accepted successfully",
      }),
    )
  }

  async reachedAtStop(tripId: number, stopId: number) {
    const existingTrip = (await this.tripRepository.getByParams({
      where: { id: tripId },
      findOne: true,
    })) as Trip

    const existingStop = (await this.tripIntermediateStopRepository.getByParams(
      {
        where: { id: stopId },
        findOne: true,
      },
    )) as TripIntermediateStop

    if (!existingTrip || !existingStop) {
      return failureResponse(code.VALIDATION, "Trip or stop does not exist")
    }

    await this.tripRepository.save(
      {
        current_stop_id: stopId,
      },
      {
        id: tripId,
      },
    )

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_update, {
        ":data": `Trip reached at ${existingStop.stop_address || "the specified stop"} successfully`,
      }),
    )
  }

  async completeTrip(tripId: number, driverId: number) {
    const existingAssignment = await this.tripFleetAssignmentRepository.findOne(
      {
        where: {
          driver_id: driverId,
          trip_id: tripId,
        },
        relations: [
          "trip",
          "trip.customer",
          "trip.customer.dispatcher",
          "trip.customer.dispatcher.users",
          "driver",
          "fleet",
        ],
      },
    )

    if (!existingAssignment) {
      return failureResponse(
        code.VALIDATION,
        errorMessage(messageKey.data_not_found, {
          ":data": "Trip",
        }),
      )
    }

    const isProperVehicle = await this.fleetManagementRepository.getByParams({
      where: {
        id: existingAssignment.fleet_id,
        assigned_driver: driverId,
      },
      findOne: true,
    })

    if (isEmpty(isProperVehicle)) {
      return failureResponse(
        code.VALIDATION,
        `Vehicle Mismatch : Switch to ${existingAssignment?.fleet?.registration_number} to start this trip`,
      )
    }

    const trip_completed_at = new Date()

    await this.tripFleetAssignmentRepository.update(
      {
        driver_id: driverId,
        trip_id: tripId,
      },
      {
        trip_completed_at: trip_completed_at,
      },
    )

    await this.tripRepository.save(
      {
        status: STATUS.COMPLETED,
        trip_end_time: trip_completed_at,
      },
      { id: tripId },
    )

    // Save timeline history for completion
    await this.tripTimelineHistoryRepository.save({
      trip_id: tripId,
      status: STATUS.COMPLETED,
      previous_status: existingAssignment.trip.status,
      changed_by_id: driverId || null,
    })

    const tripDetails: any = await this.tripRepository.getByParams({
      where: {
        id: tripId,
      },
      select: ["id", "trip_start_time", "trip_end_time", "plan_id"],
      relations: [
        "plan:id.charge_type:id,unit_of_measure",
        "base_pricing:id,vehicle_type_price",
      ],
      findOne: true,
    })

    const tripBasePricingDetails: any =
      await this.tripService.getTripBasePrice(tripId)

    let durationMinutes = 0

    const tripBasePricing: any =
      await this.tripBasePricingRepository.getByParams({
        where: {
          trip_id: tripId,
        },
        findOne: true,
      })

    const isTripAddOnExists = await this.tripAddOnRepository.getByParams({
      where: {
        trip_id: tripId,
      },
    })

    if (tripBasePricingDetails?.data?.charge_type?.unit_of_measure === "min") {
      const start = moment(
        tripDetails.trip_start_time,
        "YYYY-MM-DD HH:mm:ss.SSS",
      )

      const end = moment(tripDetails.trip_end_time, "YYYY-MM-DD HH:mm:ss.SSS")

      const basePricePerHour =
        tripBasePricingDetails?.data?.vehicle_type_price || 0

      durationMinutes = Math.ceil(moment.duration(end.diff(start)).asMinutes())

      const fullQuarters = Math.floor(durationMinutes / 15)
      const pricePerQuarter = basePricePerHour / 4
      const finalPrice = fullQuarters * pricePerQuarter

      if (isEmpty(isTripAddOnExists)) {
        const isTripAddOnPricingSet =
          await this.tripAddOnPricingRepository.getByParams({
            where: {
              trip_id: tripId,
            },
            findOne: true,
          })

        if (isEmpty(isTripAddOnPricingSet)) {
          const tripAddOnPrice: any =
            await this.tripService.getSelectedAddOnsForTrip(tripId)

          const addons = tripAddOnPrice?.data || []

          const payloads = addons.map((addon) => {
            const price = Number(addon.price) || 0
            const quantity = Number(addon.quantity) || 1

            return {
              trip_id: tripId,
              addons_id: addon.addon_id,
              price,
              quantity,
              total_price: price * quantity,
            }
          })

          await this.tripAddOnPricingRepository.save(payloads)
        }
      }

      if (!isEmpty(tripBasePricing)) {
        await this.tripBasePricingRepository.save(
          { vehicle_total_price: finalPrice },
          { id: tripBasePricing.id },
        )
      } else {
        const payload = {
          trip_id: tripId,
          vehicle_total_price: finalPrice,
          vehicle_type_id: tripBasePricingDetails?.data?.vehicle_type_id,
          trip_type_id: tripBasePricingDetails?.data?.trip_type_id,
          meet_greet_id: tripBasePricingDetails?.data?.meet_greet_id,
          meet_greet_price: tripBasePricingDetails?.data?.meet_greet_price,
          vehicle_type_price: tripBasePricingDetails?.data?.vehicle_type_price,
        }

        await this.tripBasePricingRepository.save(payload)
      }
    }

    const vehicleStatus = await this.vehicleStatusService.getStatusFromName(
      VEHICLE_STATUS.Operational,
    )

    if (vehicleStatus) {
      const vehicleStatusId = vehicleStatus.id
      await this.fleetManagementRepository.save(
        { vehicle_status_id: vehicleStatusId },
        {
          id: existingAssignment.fleet_id,
        },
      )
    }

    const dispatcherId =
      existingAssignment?.trip?.customer?.dispatcher?.users[0]?.id
    if (dispatcherId) {
      this.notificationService.sendNotification({
        user_ids: [dispatcherId],
        title: "Trip completed by driver",
        message: formatMessage("driverCompletedTrip", {
          TRIP_ID: existingAssignment?.trip_id?.toString(),
          CUSTOMER: existingAssignment?.trip?.customer?.customer_name,
          DRIVER: existingAssignment?.driver?.first_name,
        }),
        data: {
          type: "trip",
          trip_id: existingAssignment?.trip_id,
          trip_status: STATUS.COMPLETED,
        },
      })
    }

    // this.sendCustomerNotification(tripId, "tripEnd", [
    //   existingAssignment.trip.customer_id,
    // ])

    this.sendCustomerNotification(
      tripId,
      "tripEnd",
      [existingAssignment.trip.customer_id],
      undefined,
      STATUS.COMPLETED,
      existingAssignment.driver_id,
      existingAssignment?.driver?.first_name +
        " " +
        existingAssignment?.driver?.last_name,
    )

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_update, {
        ":data": "Trip completed successfully",
      }),
    )
  }

  async startTrip(tripId: number, driverId: number, otp: number) {
    const ongoingTrip = await this.tripFleetAssignmentRepository
      .createQueryBuilder("tfa")
      .select("tfa.trip_id")
      .innerJoin("tfa.trip", "t")
      .where("tfa.driver_id = :driverId", { driverId })
      .andWhere("t.id != :tripId", { tripId })
      .andWhere("t.status IN (:...statuses)", {
        statuses: [STATUS.ONGOING, STATUS.PICKUP],
      })
      .andWhere("tfa.released_at IS NULL")
      .andWhere("tfa.trip_completed_at IS NULL")
      .andWhere("t.deleted_at IS NULL")
      .limit(1)
      .getRawOne()

    if (ongoingTrip) {
      return failureResponse(
        code.VALIDATION,
        "Driver already has an ongoing trip. Cannot start a new trip.",
      )
    }

    const existingAssignment = await this.tripFleetAssignmentRepository.findOne(
      {
        where: { trip_id: tripId },
        relations: [
          "trip",
          "trip.customer",
          "trip.customer.dispatcher",
          "trip.customer.dispatcher.users",
          "driver",
          "fleet",
        ],
      },
    )

    if (!existingAssignment) {
      return failureResponse(
        code.VALIDATION,
        errorMessage(messageKey.data_not_found, {
          ":data": "Trip",
        }),
      )
    }

    const isProperVehicle = await this.fleetManagementRepository.getByParams({
      where: {
        id: existingAssignment.fleet_id,
        assigned_driver: driverId,
      },
      findOne: true,
    })

    if (isEmpty(isProperVehicle)) {
      return failureResponse(
        code.VALIDATION,
        `Vehicle Mismatch : Switch to ${existingAssignment?.fleet?.registration_number} to start this trip`,
      )
    }

    const isDriverOffDuty = await this.teamMemberRepository.getByParams({
      where: {
        id: driverId,
        duty_status: dutyStatus.OFF_DUTY,
      },
      findOne: true,
    })

    if (isDriverOffDuty) {
      return failureResponse(
        code.VALIDATION,
        "Driver is currently off duty. Cannot start the trip.",
      )
    }

    const pickupDate = formateDate(
      existingAssignment.trip.pickup_datetime,
      "YYYY-MM-DD",
    )
    const currentDate = formateDate(new Date(), "YYYY-MM-DD")

    if (pickupDate < currentDate) {
      return failureResponse(
        code.VALIDATION,
        "Trip pickup time has already passed. Cannot start the trip.",
      )
    }

    if (existingAssignment.trip.is_trip_otp_required) {
      if (existingAssignment.trip.trip_otp != otp) {
        return failureResponse(code.VALIDATION, "Invalid OTP")
      }
    }

    // Ensure this trip is assigned to the specified driver
    if (existingAssignment.driver_id !== driverId) {
      return failureResponse(
        code.VALIDATION,
        "Driver is not assigned to this trip.",
      )
    }

    await this.tripRepository.save(
      { status: STATUS.ONGOING, trip_start_time: new Date() },
      { id: tripId },
    )

    const vehicleStatus = await this.vehicleStatusService.getStatusFromName(
      VEHICLE_STATUS.Reserve,
    )
    if (vehicleStatus) {
      const vehicleStatusId = vehicleStatus.id
      await this.fleetManagementRepository.save(
        { vehicle_status_id: vehicleStatusId },
        {
          id: existingAssignment.fleet_id,
        },
      )
    }

    const intermediateStop =
      (await this.tripIntermediateStopRepository.getByParams({
        where: {
          trip_id: tripId,
        },
        findOne: true,
      })) as TripIntermediateStop

    const dispatcherId =
      existingAssignment?.trip?.customer?.dispatcher?.users[0]?.id
    let notificationMessage = "Driver started trip"
    if (intermediateStop) {
      notificationMessage = formatMessage("driverStartedRideToStop", {
        TRIP_ID: existingAssignment?.trip_id?.toString(),
        CUSTOMER: existingAssignment?.trip?.customer?.customer_name,
        DRIVER: `${existingAssignment?.driver?.first_name}${existingAssignment?.driver?.last_name}`,
      })
    } else {
      notificationMessage = formatMessage("driverStartedRideToLocation", {
        TRIP_ID: existingAssignment?.trip_id?.toString(),
        CUSTOMER: existingAssignment?.trip?.customer?.customer_name,
        DRIVER: `${existingAssignment?.driver?.first_name}${existingAssignment?.driver?.last_name}`,
      })
    }
    if (dispatcherId) {
      this.notificationService.sendNotification({
        user_ids: [dispatcherId],
        title: "Driver started trip",
        message: notificationMessage,
        data: {
          type: "trip",
          trip_id: existingAssignment?.trip_id,
          trip_status: STATUS.ONGOING,
        },
      })
    }

    this.sendCustomerNotification(
      tripId,
      "otpVerified",
      [existingAssignment?.trip?.customer_id],
      existingAssignment?.trip?.drop_off_location,
      STATUS.ONGOING,
    )

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_update, {
        ":data": "Trip started successfully",
      }),
    )
  }

  async getCancelledTripsCount(
    driverId: number,
    start_timestamp?: string,
    end_timestamp?: string,
  ) {
    let assignedTripsQuery = this.tripFleetAssignmentRepository
      .createQueryBuilder("tfa")
      .select("trip.id")
      .innerJoin("tfa.trip", "trip")
      .where("tfa.driver_id = :driverId", { driverId })
      .andWhere("tfa.trip_id IS NOT NULL")
      .andWhere("trip.deleted_at IS NULL")

    if (start_timestamp && end_timestamp) {
      assignedTripsQuery = assignedTripsQuery.andWhere(
        "DATE(trip.pickup_datetime) BETWEEN DATE(:start) AND DATE(:end)",
        {
          start: start_timestamp,
          end: end_timestamp,
        },
      )
    }

    const assignedTrips = await assignedTripsQuery
      .select("COUNT(DISTINCT trip.id)", "assignedTrips")
      .getRawOne()

    let query = this.tripCancelRepository["entity"]
      .createQueryBuilder("cancellation")
      .select("trip.id")
      .innerJoin(
        TripFleetAssignment,
        "assignment",
        "assignment.trip_id = cancellation.trip_id",
      )
      .innerJoin("assignment.trip", "trip")
      .where("assignment.driver_id = :driverId", { driverId })
      .andWhere("trip.deleted_at IS NULL")

    if (start_timestamp && end_timestamp) {
      query = query.andWhere(
        "DATE(trip.pickup_datetime) BETWEEN DATE(:start) AND DATE(:end)",
        {
          start: start_timestamp,
          end: end_timestamp,
        },
      )
    }

    const cancelledTrips = await query
      .select("COUNT(DISTINCT trip.id)", "cancelledTrips")
      .getRawOne()

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, {
        ":data": "Cancelled trips count",
      }),
      {
        cancelledTrips: Number(cancelledTrips?.cancelledTrips || 0),
        assignedTrips: Number(assignedTrips?.assignedTrips || 0),
      },
    )
  }

  async getRating(
    driverId: number,
    start_timestamp?: string,
    end_timestamp?: string,
  ) {
    let ratingQuery = this.ratingRepository["entity"]
      .createQueryBuilder("rating")
      .innerJoin("rating.trip", "trip")
      .where("rating.rated_id = :driverId", { driverId })
      .andWhere("rating.rating_type = :ratingType", {
        ratingType: RATING_TYPES.CUSTOMER_TO_DRIVER,
      })

    if (start_timestamp && end_timestamp) {
      ratingQuery = ratingQuery.andWhere(
        "DATE(trip.pickup_datetime) BETWEEN DATE(:start) AND DATE(:end)",
        {
          start: start_timestamp,
          end: end_timestamp,
        },
      )
    }

    const ratingsWithTags = await ratingQuery.getMany()

    const averageRating =
      ratingsWithTags.length > 0
        ? parseFloat(
            (
              ratingsWithTags.reduce((sum, r) => sum + r.rating, 0) /
              ratingsWithTags.length
            ).toFixed(1),
          )
        : 0

    // Get rating count
    const ratingCount = ratingsWithTags.length

    // Calculate top tags
    const tagCount: Record<string, number> = {}
    ratingsWithTags.forEach((r) => {
      if (Array.isArray(r.tags)) {
        r.tags.forEach((tag) => {
          tagCount[tag] = (tagCount[tag] || 0) + 1
        })
      }
    })

    const topTags = Object.entries(tagCount)
      .sort((a, b) => b[1] - a[1])
      .slice(0, 3)
      .map(([tag]) => tag)

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, {
        ":data": "Rating",
      }),
      { ratingCount, averageRating, topTags },
    )
  }

  async getLanguagesDropdown() {
    const languages = await this.languageRepository.getByParams({
      select: ["id", "name"],
      orderBy: { name: "ASC" },
    })
    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, {
        ":data": "Language",
      }),
      languages,
    )
  }

  async getHomePageCounts(dispatcherId: number) {
    try {
      // Get total trips
      const tripsQuery = this.tripRepository["entity"]
        .createQueryBuilder("trip")
        .innerJoin("trip.customer", "customer")
        .where("customer.dispatcher_id = :dispatcherId", { dispatcherId })
        .andWhere("trip.deleted_at IS NULL")

      const totalTrips = await tripsQuery.getCount()

      // Get trips count for current month
      const currentMonthTripsQuery = this.tripRepository["entity"]
        .createQueryBuilder("trip")
        .innerJoin("trip.customer", "customer")
        .where("customer.dispatcher_id = :dispatcherId", { dispatcherId })
        .andWhere("trip.deleted_at IS NULL")
        .andWhere(
          "DATE(trip.pickup_datetime) >= DATE(date_trunc('month', CURRENT_DATE))",
        )
        .andWhere(
          "DATE(trip.pickup_datetime) < DATE(date_trunc('month', CURRENT_DATE) + interval '1 month')",
        )

      const currentMonthTrips = await currentMonthTripsQuery.getCount()

      // Get trips count for previous month
      const previousMonthTripsQuery = this.tripRepository["entity"]
        .createQueryBuilder("trip")
        .innerJoin("trip.customer", "customer")
        .where("customer.dispatcher_id = :dispatcherId", { dispatcherId })
        .andWhere("trip.deleted_at IS NULL")
        .andWhere(
          "DATE(trip.pickup_datetime) >= DATE(date_trunc('month', CURRENT_DATE - interval '1 month'))",
        )
        .andWhere(
          "DATE(trip.pickup_datetime) < DATE(date_trunc('month', CURRENT_DATE))",
        )

      const previousMonthTrips = await previousMonthTripsQuery.getCount()

      // Calculate percentage increase
      let tripsPercentageIncrease = 0
      if (previousMonthTrips > 0) {
        tripsPercentageIncrease =
          ((currentMonthTrips - previousMonthTrips) / previousMonthTrips) * 100
      } else if (currentMonthTrips > 0) {
        tripsPercentageIncrease = 100 // If no previous month trips but current month has trips
      }

      // Get total customers
      const customersQuery = this.customerRepository["entity"]
        .createQueryBuilder("customer")
        .where("customer.dispatcher_id = :dispatcherId", { dispatcherId })
        .andWhere("customer.deleted_at IS NULL")

      const totalCustomers = await customersQuery.getCount()

      const customerInTripTodayCount = await this.tripRepository["entity"]
        .createQueryBuilder("trip")
        .innerJoin("trip.customer", "customer")
        .where("customer.dispatcher_id = :dispatcherId", { dispatcherId })
        .andWhere("customer.deleted_at IS NULL")
        .andWhere("trip.deleted_at IS NULL")
        .andWhere("DATE(trip.pickup_datetime) = :pickupDate", {
          pickupDate: moment(new Date()).format("YYYY-MM-DD"),
        })
        .select("customer.id")
        .groupBy("customer.id")
        .getCount()

      // Get total drivers
      const driversQuery = this.teamMemberEntity
        .createQueryBuilder("driver")
        .innerJoin("driver.role", "role")
        .where("driver.reporting_to_id = :dispatcherId", { dispatcherId })
        .andWhere("LOWER(role.name) = :roleName", { roleName: "driver" })
        .andWhere("driver.deleted_at IS NULL")

      const totalDrivers = await driversQuery.getCount()

      const driverInTripTodayCount = await this.tripRepository["entity"]
        .createQueryBuilder("trip")
        .innerJoin("trip.assignments", "assignments")
        .innerJoin("assignments.driver", "driver")
        .where("driver.reporting_to_id = :dispatcherId", { dispatcherId })
        .andWhere("driver.deleted_at IS NULL")
        .andWhere("trip.deleted_at IS NULL")
        .andWhere("DATE(trip.pickup_datetime) = :pickupDate", {
          pickupDate: moment(new Date()).format("YYYY-MM-DD"),
        })
        .select("driver.id")
        .groupBy("driver.id")
        .getCount()

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, {
          ":data": "Home page counts",
        }),
        {
          totalTrips,
          currentMonthTrips,
          previousMonthTrips,
          tripsPercentageIncrease:
            Math.round(tripsPercentageIncrease * 100) / 100, // Round to 2 decimal places
          totalCustomers,
          customerEnRought: Number(customerInTripTodayCount || 0),
          driverOccupied: Number(driverInTripTodayCount || 0),
          totalDrivers,
        },
      )
    } catch (error) {
      console.error("Error in getHomePageCounts:", error)
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }

  async sendMessageToDriver(
    dispatcherId: number,
    sendMessageDto: SendMessageDto,
  ) {
    try {
      // Verify dispatcher exists and sender is actually the dispatcher
      const dispatcher = await this.teamMemberRepository.getByParams({
        where: { id: dispatcherId },
        findOne: true,
      })

      if (!dispatcher) {
        return failureResponse(
          code.DATA_NOT_FOUND,
          errorMessage(messageKey.data_not_found, { ":data": "Dispatcher" }),
        )
      }

      // Verify receiver exists and is a driver under this dispatcher
      const receiver = (await this.teamMemberRepository.getByParams({
        where: { id: sendMessageDto.receiver_id },
        relations: ["reporting_to"],
        findOne: true,
      })) as TeamMember

      if (!receiver || (receiver as any).reporting_to_id !== dispatcherId) {
        return failureResponse(
          code.VALIDATION,
          "Driver not found or not under this dispatcher",
        )
      }

      // Save message
      const message = await this.chatMessageRepository.save({
        sender_id: dispatcherId,
        receiver_id: sendMessageDto.receiver_id,
        message_text: sendMessageDto.message,
        is_read: false,
      })

      // Update conversation
      await this.chatConversationRepository.findOrCreateConversation(
        dispatcherId,
        sendMessageDto.receiver_id,
      )

      await this.chatConversationRepository.updateUnreadCount(
        sendMessageDto.receiver_id,
        false, // isDispatcher = false for driver
      )

      return successResponse(
        code.SUCCESS,
        successMessage("Message sent successfully", {}),
        {
          id: message.id,
          sender_id: dispatcherId,
          receiver_id: sendMessageDto.receiver_id,
          message: sendMessageDto.message,
          created_at: message.created_at,
        },
      )
    } catch (error) {
      console.error("Error sending message to driver:", error)
      return failureResponse(500, messageKey.something_went_wrong)
    }
  }

  async getDriverDispatcher(driverId: number) {
    try {
      const driver = (await this.teamMemberRepository.getByParams({
        where: { id: driverId },
        relations: ["reporting_to"],
        findOne: true,
      })) as TeamMember

      if (!driver || !(driver as any).reporting_to) {
        return failureResponse(
          code.DATA_NOT_FOUND,
          errorMessage(messageKey.data_not_found, { ":data": "Dispatcher" }),
        )
      }

      const dispatcherInfo = (driver as any).reporting_to

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, {
          ":data": "dispatcher",
        }),
        {
          id: dispatcherInfo.id,
          first_name: dispatcherInfo.first_name,
          last_name: dispatcherInfo.last_name,
          phone_number: dispatcherInfo.phone_number,
          profile_photo: dispatcherInfo.profile_photo,
        },
      )
    } catch (error) {
      console.error("Error getting driver dispatcher:", error)
      return failureResponse(500, messageKey.something_went_wrong)
    }
  }

  async sendMessageToDispatcher(sendMessageDto: SendMessageDto) {
    try {
      // Get driver info to verify dispatcher relationship
      const driver = (await this.teamMemberRepository.getByParams({
        where: { id: sendMessageDto.receiver_id },
        relations: ["reporting_to"],
        findOne: true,
      })) as TeamMember

      if (!driver) {
        return failureResponse(
          code.DATA_NOT_FOUND,
          errorMessage(messageKey.data_not_found, { ":data": "Dispatcher" }),
        )
      }

      // Verify the dispatcher is actually assigned to this driver
      if ((driver as any).reporting_to_id !== sendMessageDto.receiver_id) {
        return failureResponse(
          code.VALIDATION,
          "Dispatcher not found for this driver",
        )
      }

      // Save message
      const message = await this.chatMessageRepository.save({
        sender_id: sendMessageDto.receiver_id, // This should be the driver's ID from auth
        receiver_id: sendMessageDto.receiver_id,
        message_text: sendMessageDto.message,
        is_read: false,
      })

      // Update conversation
      await this.chatConversationRepository.findOrCreateConversation(
        sendMessageDto.receiver_id,
        sendMessageDto.receiver_id,
      )

      await this.chatConversationRepository.updateUnreadCount(
        sendMessageDto.receiver_id,
        true, // isDispatcher = true
      )

      return successResponse(
        code.SUCCESS,
        successMessage("Message sent successfully", {}),
        {
          id: message.id,
          sender_id: sendMessageDto.receiver_id,
          receiver_id: sendMessageDto.receiver_id,
          message: sendMessageDto.message,
          created_at: message.created_at,
        },
      )
    } catch (error) {
      console.error("Error sending message to dispatcher:", error)
      return failureResponse(500, messageKey.something_went_wrong)
    }
  }

  async getChatRoom(data: {
    current_user_id: number
    current_user_type: string
    target_user_id: number
    target_user_type: string
    trip_id?: number
  }) {
    try {
      const {
        current_user_id,
        current_user_type = "team_member",
        target_user_id,
        target_user_type = "team_member",
        trip_id,
      } = data

      if (!current_user_id || !target_user_id) {
        return failureResponse(
          code.VALIDATION,
          "Missing required fields: current_user_id, target_user_id",
        )
      }

      // Validate user types
      const validUserTypes = ["team_member", "customer"]
      if (!validUserTypes.includes(current_user_type)) {
        return failureResponse(
          code.VALIDATION,
          "Invalid current_user_type. Must be 'team_member' or 'customer'",
        )
      }

      if (!validUserTypes.includes(target_user_type)) {
        return failureResponse(
          code.VALIDATION,
          "Invalid target_user_type. Must be 'team_member' or 'customer'",
        )
      }

      const chatRoom =
        await this.chatRoomRepository.findChatRoomBetweenParticipants(
          current_user_type,
          current_user_id,
          target_user_type,
          target_user_id,
          trip_id,
        )

      if (!chatRoom || !chatRoom?.id) {
        return failureResponse(code.DATA_NOT_FOUND, "Chat room not found")
      }

      // Get chat history for this room with error handling
      let history
      try {
        history = await this.chatMessageRepository.getChatHistory(
          chatRoom.id,
          50,
          0,
        )
      } catch (historyError) {
        console.error("Error getting chat history:", historyError)
        return failureResponse(code.ERROR, "Error getting chat history")
      }
      return successResponse(
        code.SUCCESS,
        "Chat history retrieved successfully",
        {
          chat_room_id: chatRoom.id,
          chat_type: chatRoom.chat_type,
          history: history,
        },
      )
    } catch (error) {
      console.error("Unexpected error in handleJoinChatRoom:", error)
      return failureResponse(500, messageKey.something_went_wrong)
    }
  }

  async getChatHistory(chatHistoryDto: ChatHistoryDto) {
    try {
      const { chat_room_id, limit = 50, skip = 0 } = chatHistoryDto

      const messages = await this.chatMessageRepository.getChatHistory(
        chat_room_id,
        limit,
        skip,
      )

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, {
          ":data": "chat history",
        }),
        messages,
      )
    } catch (error) {
      console.error("Error getting chat history:", error)
      return failureResponse(500, messageKey.something_went_wrong)
    }
  }

  async markMessagesAsRead(receiver_id: number, chat_room_id: number) {
    try {
      // Get user participant type in this chat room
      const userParticipant =
        (await this.chatParticipantRepository.findByParams({
          where: {
            chat_room_id: chat_room_id,
            participant_id: receiver_id,
          },
          findOne: true,
        })) as ChatParticipant

      if (!userParticipant) {
        return failureResponse(
          code.VALIDATION,
          "User not a participant in this chat",
        )
      }

      // Mark messages as read
      await this.chatMessageRepository.markMessagesAsRead(
        chat_room_id,
        receiver_id,
        userParticipant.participant_type,
      )

      return successResponse(
        code.SUCCESS,
        successMessage("Messages marked as read", {}),
      )
    } catch (error) {
      console.error("Error marking messages as read:", error)
      return failureResponse(500, messageKey.something_went_wrong)
    }
  }

  async getDispatcherChatContacts(
    dispatcherId: number,
    type: "driver" | "customer",
  ) {
    try {
      // Determine chat type and participant type based on filter
      const chatType =
        type === "driver" ? "driver_dispatcher" : "customer_dispatcher"
      const participantType = type === "driver" ? "team_member" : "customer"

      // Get all chat rooms where dispatcher is a participant
      const chatRooms = await this.chatRoomRepository.findByParams({
        where: { chat_type: chatType },
        relations: ["participants", "messages"],
      })

      // Ensure chatRooms is an array
      const chatRoomsArray = Array.isArray(chatRooms) ? chatRooms : [chatRooms]

      // Filter rooms where dispatcher is a participant
      const dispatcherRooms = chatRoomsArray.filter((room) =>
        room.participants.some(
          (p) =>
            p.participant_type === "team_member" &&
            p.participant_id === dispatcherId,
        ),
      )

      if (dispatcherRooms.length === 0) {
        return successResponse(code.SUCCESS, "No chat contacts found", [])
      }

      // Get unique contact IDs (excluding dispatcher)
      const contactIds = new Set<number>()
      dispatcherRooms.forEach((room) => {
        room.participants.forEach((participant) => {
          if (
            participant.participant_type === participantType &&
            participant.participant_id !== dispatcherId
          ) {
            contactIds.add(participant.participant_id)
          }
        })
      })

      if (contactIds.size === 0) {
        return successResponse(code.SUCCESS, "No chat contacts found", [])
      }

      const contacts = []

      for (const contactId of contactIds) {
        // Get contact details
        let contact
        if (type === "driver") {
          const driverRole = (await this.roleRepository.getByParams({
            whereLower: { name: "driver" },
            findOne: true,
          })) as Role

          if (!driverRole) {
            return failureResponse(404, messageKey.data_not_found)
          }
          contact = await this.teamMemberRepository.getByParams({
            where: { id: contactId, role_id: driverRole.id },
            relations: ["role"],
            select: [
              "id",
              "first_name",
              "last_name",
              "phone_number",
              "profile_photo",
            ],
            findOne: true,
          })
        } else {
          contact = await this.customerRepository.getByParams({
            where: { id: contactId },
            select: [
              "id",
              "customer_name",
              "primary_address",
              "phone_number",
              "profile_photo",
            ],
            findOne: true,
          })
        }

        if (!contact) continue

        // Find the chat room between dispatcher and this contact
        const chatRoom = dispatcherRooms.find((room) =>
          room.participants.some(
            (p) =>
              p.participant_type === participantType &&
              p.participant_id === contactId,
          ),
        )

        if (!chatRoom) continue

        // Get last message
        const lastMessage =
          chatRoom.messages.length > 0
            ? chatRoom.messages.sort(
                (a, b) => b.created_at.getTime() - a.created_at.getTime(),
              )[0]
            : null

        // Get unread count for dispatcher
        const unreadCount = chatRoom.messages.filter(
          (msg) => msg.receiver_id === dispatcherId && !msg.is_read,
        ).length

        const contactData = {
          ...contact,
          last_message: lastMessage?.message_text || null,
          last_message_time: lastMessage?.created_at || null,
          unread_count: unreadCount,
        }

        contacts.push(contactData)
      }

      // Sort by last message time (most recent first)
      contacts.sort((a, b) => {
        const aTime = a.last_message_time
          ? new Date(a.last_message_time).getTime()
          : 0
        const bTime = b.last_message_time
          ? new Date(b.last_message_time).getTime()
          : 0
        return bTime - aTime
      })

      return successResponse(
        code.SUCCESS,
        "Chat contacts retrieved successfully",
        contacts,
      )
    } catch (error) {
      console.error("Error getting dispatcher chat contacts:", error)
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }

  private async sendCustomerNotification(
    tripId: number,
    messageKey: string,
    customerId: number[],
    drop_location?: string,
    status?: string,
    driverId?: number,
    driverName?: string,
  ) {
    let message = ""
    let title = ""
    if (messageKey === "otpVerified") {
      message = formatMessage("otpVerified", {
        DROP_LOCATION: drop_location,
      })
      title = "Trip started"
    } else if (messageKey === "tripEnd") {
      message = formatMessage("tripEnd", {})
      title = "Trip ended"
    }

    this.notificationService.sendCustomerNotification({
      user_ids: customerId,
      title,
      message,
      data: {
        trip_id: tripId,
        type: "trip",
        status: status,
        driver_id: driverId,
        driver_name: driverName,
      },
    })
  }
}
