import { Injectable } from "@nestjs/common"
import { ConfigService } from "@nestjs/config"
import { failureResponse, successResponse } from "src/common/response/response"
import { code } from "src/common/response/response.code"
import { messageKey } from "src/constants/message-keys"
import { errorMessage, isEmpty, successMessage } from "src/utils/helpers"
import { TeamMemberRepository } from "../../team-member/repositories/team_member.repository"
import { CreateBulkDriverAvailabilityDto } from "../dto/create-driver-availability.dto"
import { UpdateDriverAvailabilityDto } from "../dto/update-driver-availability.dto"
import { DriverAvailabilityRepository } from "../repositories/driver-availability.repository"
import moment from "moment"
import { DriverAvailabilityHistoryRepository } from "../repositories/driver_availability_history.repository"
import { IsNull, Repository } from "typeorm"
import { InjectRepository } from "@nestjs/typeorm"
import { DriverAvailabilityHistory } from "../entities/driver-availability-history.entity"

@Injectable()
export class DriverAvailabilityService {
  constructor(
    private readonly driverAvailabilityRepository: DriverAvailabilityRepository,
    private readonly driverAvailabilityHistoryRepository: DriverAvailabilityHistoryRepository,
    private readonly teamMemberRepository: TeamMemberRepository,
    @InjectRepository(DriverAvailabilityHistory)
    private readonly driverAvailabilityHistoryModelRepository: Repository<DriverAvailabilityHistory>,
    private readonly configService: ConfigService,
  ) {}

  async bulkCreate(createDto: CreateBulkDriverAvailabilityDto) {
    try {
      // Validate input
      if (isEmpty(createDto.availability)) {
        return failureResponse(
          code.BAD_REQUEST,
          errorMessage(messageKey.data_not_found, {
            ":data": "availability data",
          }),
        )
      }

      const driverId = createDto.availability[0]?.driver_id

      // Check if driver exists
      const driver = await this.teamMemberRepository.getByParams({
        where: { id: driverId },
        findOne: true,
      })

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

      // Fetch existing availability records for the driver
      const existingRecords =
        (await this.driverAvailabilityRepository.getByParams({
          where: { driver_id: driverId },
          select: [
            "id",
            "driver_id",
            "day_of_week",
            "start_time",
            "end_time",
            "is_available",
          ],
        })) as any

      // Create a map for easy lookup of existing records by day_of_week
      const existingMap = new Map(
        existingRecords.map((record: any) => [record.day_of_week, record]),
      ) as any

      // Filter records that have actually changed
      const changedRecords = createDto.availability.filter((incoming) => {
        const existing = existingMap.get(incoming.day_of_week)

        // If no existing record, it's a new record (changed)
        if (!existing) {
          return true
        }

        // Check if any field has changed
        const hasChanged =
          existing.start_time !== incoming.start_time ||
          existing.end_time !== incoming.end_time ||
          existing.is_available !== incoming.is_available

        return hasChanged
      })

      // Remove existing records for days that are being updated
      const recordsToRemove = changedRecords
        .filter((record) => existingMap.has(record.day_of_week))
        .map((record) => ({
          driver_id: record.driver_id,
          day_of_week: record.day_of_week,
        }))

      if (recordsToRemove.length > 0) {
        await Promise.all(
          recordsToRemove.map((condition) =>
            this.driverAvailabilityRepository.remove(condition),
          ),
        )
      }

      await this.driverAvailabilityRepository.saveMany(changedRecords)

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_add, {
          ":data": "Driver Availability",
        }),
      )
    } catch (error) {
      return failureResponse(
        code.ERROR,
        errorMessage(messageKey.something_went_wrong),
      )
    }
  }

  async findAll({
    driver_id,
    created_at,
  }: {
    driver_id?: number
    created_at?: string
  }) {
    try {
      const filter: any = {}

      if (driver_id) {
        filter.driver_id = driver_id
      }

      const inputDate = created_at ? new Date(created_at) : new Date()

      // const startOfWeek = moment
      //   .utc(inputDate)
      //   .isoWeekday(1)
      //   .startOf("day")
      //   .toDate()

      // const endOfWeek = moment
      //   .utc(inputDate)
      //   .isoWeekday(7)
      //   .endOf("day")
      //   .toDate()

      // filter.created_at = {
      //   gte: startOfWeek,
      //   lte: endOfWeek,
      // }

      const startOfWeek = moment
        .utc(inputDate)
        .isoWeekday(1) // Monday
        .startOf("day")

      const endOfWeek = startOfWeek
        .clone()
        .add(6, "days") // Saturday; use 7 for Sunday
        .endOf("day")

      filter.created_at = {
        gte: startOfWeek.toDate(),
        lte: endOfWeek.toDate(),
      }

      const queryParams: any = {
        select: [
          "id",
          "driver_id",
          "day_of_week",
          "start_time",
          "end_time",
          "is_available",
          "created_at",
        ],
        where: filter,
      }

      const result =
        await this.driverAvailabilityRepository.getByParams(queryParams)

      if (isEmpty(result)) {
        return failureResponse(
          code.SUCCESS,
          errorMessage(messageKey.data_not_found, {
            ":data": "Driver Availability",
          }),
        )
      }

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, {
          ":data": "Driver Availability",
        }),
        result,
      )
    } catch (error) {
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }

  async findOne(id: number) {
    try {
      const availability = await this.driverAvailabilityRepository.getByParams({
        where: { id },
        findOne: true,
      })

      if (isEmpty(availability)) {
        return failureResponse(
          code.SUCCESS,
          errorMessage(messageKey.data_not_found, {
            ":data": "Driver Availability",
          }),
        )
      }

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, {
          ":data": "Driver Availability",
        }),
        availability,
      )
    } catch (error) {
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }

  async update(id: number, updateDto: UpdateDriverAvailabilityDto) {
    try {
      const existing = (await this.driverAvailabilityRepository.getByParams({
        where: { id },
        findOne: true,
      })) as any

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

      await this.driverAvailabilityRepository.save({
        ...existing,
        ...updateDto,
      })

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_update, {
          ":data": "Driver Availability",
        }),
      )
    } catch (error) {
      return failureResponse(
        code.ERROR,
        errorMessage(messageKey.something_went_wrong),
      )
    }
  }

  async remove(id: number) {
    try {
      const record = await this.driverAvailabilityRepository.getByParams({
        where: { id },
        findOne: true,
      })

      if (isEmpty(record)) {
        return failureResponse(
          code.DATA_NOT_FOUND,
          errorMessage(messageKey.data_not_found, {
            ":data": "Driver Availability",
          }),
        )
      }

      await this.driverAvailabilityRepository.remove({ id })

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_removed, {
          ":data": "Driver Availability",
        }),
      )
    } catch (error) {
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }

  async updateDutyStatus(driver_id: number, updateData: any) {
    try {
      const data = (await this.driverAvailabilityRepository.getByParams({
        where: { driver_id },
        findOne: true,
      })) as any

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

      const { duty_status, start_time, end_time } = updateData || {}

      if (duty_status === "off-duty" && end_time) {
        // Find last active duty record
        const existingHistory =
          (await this.driverAvailabilityHistoryModelRepository.findOne({
            where: { driver_id, end_time: IsNull() },
            order: { start_time: "DESC" },
          })) as any

        if (existingHistory) {
          existingHistory.end_time = end_time ? new Date(end_time) : null
          existingHistory.duty_status = duty_status
          await this.driverAvailabilityHistoryRepository.save(existingHistory)
        } else {
          await this.driverAvailabilityHistoryRepository.save({
            driver_id,
            duty_status,
            start_time: start_time ? new Date(start_time) : new Date(),
            end_time: end_time ? new Date(end_time) : null,
          })
        }
      } else if (duty_status === "on-duty") {
        await this.driverAvailabilityHistoryRepository.save({
          driver_id,
          duty_status,
          start_time: start_time ? new Date(start_time) : new Date(),
          end_time: null,
        })
      }

      // Update driver’s current status in team_members
      await this.teamMemberRepository.save({
        id: driver_id,
        duty_status,
      })

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_update, {
          ":data": "Driver Availability",
        }),
      )
    } catch (error) {
      return failureResponse(
        code.ERROR,
        errorMessage(messageKey.something_went_wrong),
      )
    }
  }

  async getDriverAvailabilityHistory(driver_id: number) {
    const history = await this.driverAvailabilityHistoryRepository.getByParams({
      where: { driver_id },
      orderBy: { created_at: "DESC" },
    })

    if (isEmpty(history)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, {
          ":data": "driver availability history",
        }),
      )
    }

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, {
        ":data": "driver availability history",
      }),
      history,
    )
  }
}
