import { Injectable } from "@nestjs/common"
import { CreateVehicleModelDto } from "../dto/create-vehicle-model.dto"
import { UpdateVehicleModelDto } from "../dto/update-vehicle-model.dto"
import { VehicleModel } from "../entities/vehicle-model.entity"
import { VehicleModelRepository } from "../repositories/vehicle-model.repository"
import {
  errorMessage,
  isEmpty,
  successMessage,
  validationMessage,
} from "src/utils/helpers"
import { failureResponse, successResponse } from "src/common/response/response"
import { code } from "src/common/response/response.code"
import { messageKey } from "src/constants/message-keys"
import { ConfigService } from "@nestjs/config"
import { VehicleManufacturerRepository } from "../../vehicle-manufacturer/repositories/vehicle-manufacturer.repository"
import { VehicleRepository } from "../../vehicle-type/repositories/vehicle-type.repository"
import { InjectRepository } from "@nestjs/typeorm"
import { Repository } from "typeorm"

@Injectable()
export class VehicleModelService {
  constructor(
    private vehicleModelRepository: VehicleModelRepository,
    private vehicleManufactureRepository: VehicleManufacturerRepository,
    private vehicleTypeRepository: VehicleRepository,
    private readonly configService: ConfigService,
    @InjectRepository(VehicleModel)
    private readonly vehicleModelEntityRepository: Repository<VehicleModel>,
  ) {}

  async create(createVehicleModelDto: CreateVehicleModelDto) {
    if (await this.checkVehicleModelExists(createVehicleModelDto.name)) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, {
          ":data": "vehicle model",
        }),
      )
    }

    const vehicleType = await this.vehicleTypeRepository.getByParams({
      where: {
        id: createVehicleModelDto?.vehicle_type_id,
      },
      findOne: true,
    })

    if (isEmpty(vehicleType)) {
      return failureResponse(
        code.SUCCESS,
        errorMessage(messageKey.data_not_found, { ":data": "vehicle Type" }),
      )
    }

    const vehicleManufacture =
      await this.vehicleManufactureRepository.getByParams({
        where: {
          id: createVehicleModelDto?.vehicle_manufacture_id,
        },
        findOne: true,
      })

    if (isEmpty(vehicleManufacture)) {
      return failureResponse(
        code.SUCCESS,
        errorMessage(messageKey.data_not_found, {
          ":data": "vehicle manufacture",
        }),
      )
    }

    const vehicleModel = new VehicleModel()
    Object.assign(vehicleModel, createVehicleModelDto)
    await this.vehicleModelRepository.save(vehicleModel)

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_add, { ":data": "vehicle model" }),
    )
  }

  async findAll(
    limit: string,
    skip: string,
    search?: string,
    sortBy?: string,
    sortOrder?: string,
    manufacturerIds?: number[],
    typeIds?: number[],
  ) {
    const validSortColumns = ["id", "name", "created_at", "updated_at"]

    let sortColumn: string
    let order: "ASC" | "DESC" = sortOrder === "ASC" ? "ASC" : "DESC"

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

    const defaultPagination = {
      take: this.configService.get("APP.pagination.take"),
      skip: this.configService.get("APP.pagination.skip"),
    }

    const queryParams: any = {
      take: limit || defaultPagination.take,
      skip: skip || defaultPagination.skip,
      select: [
        "id",
        "name",
        "vehicle_manufacture_id",
        "vehicle_type_id",
        "status",
        "capacity",
        "created_at",
      ],
      orderBy: { [sortColumn]: order },
      relations: ["vehicle_manufacture:id,name", "vehicle_type:id,name"],
    }

    // Build where conditions
    if (search) {
      queryParams.where = {
        ...queryParams.where,
        name: { ilike: search },
      }
    }
    const whereConditions: any = {}

    // Add search condition

    // Add manufacturer filter
    if (manufacturerIds && manufacturerIds.length > 0) {
      whereConditions.vehicle_manufacture_id = manufacturerIds
    }

    // Add type filter
    if (typeIds && typeIds.length > 0) {
      whereConditions.vehicle_type_id = typeIds
    }

    // Add where conditions to query params if any exist
    if (Object.keys(whereConditions).length > 0) {
      queryParams.whereIn = whereConditions
    }

    const vehicleModel =
      await this.vehicleModelRepository.getByParams(queryParams)

    if (isEmpty(vehicleModel)) {
      return failureResponse(
        code.SUCCESS,
        errorMessage(messageKey.data_not_found, { ":data": "Vehicle model" }),
      )
    }

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, { ":data": "Vehicle model" }),
      vehicleModel,
    )
  }

  async findOne(id: number) {
    const vehicleModel = await this.vehicleModelRepository.getByParams({
      where: { id },
      findOne: true,
      select: [
        "id",
        "name",
        "vehicle_manufacture_id",
        "vehicle_type_id",
        "status",
        "capacity",
        "created_at",
      ],
      relations: ["vehicle_manufacture:id,name", "vehicle_type:id,name"],
    })

    if (isEmpty(vehicleModel)) {
      return failureResponse(
        code.SUCCESS,
        errorMessage(messageKey.data_not_found, { ":data": "vehicle model" }),
      )
    }

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, { ":data": "vehicle model" }),
      vehicleModel,
    )
  }

  async update(id: number, updateVehicleModelDto: UpdateVehicleModelDto) {
    if (await this.checkVehicleModelExists(updateVehicleModelDto.name, id)) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, {
          ":data": "vehicle model",
        }),
      )
    }

    const vehicleModel = new VehicleModel()

    const condition = {
      id: id,
    }

    Object.assign(vehicleModel, updateVehicleModelDto)

    const updatedModel = await this.vehicleModelRepository.save(
      vehicleModel,
      condition,
    )

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_update, { ":data": "vehicle model" }),
      updatedModel,
    )
  }

  async remove(id: number) {
    try {
      const vehicleModel: any = (await this.vehicleModelRepository.getByParams({
        where: { id },
        findOne: true,
        relations: ["addOns", "fleetManagements"],
      })) as VehicleModel

      if (!vehicleModel) {
        return failureResponse(code.DATA_NOT_FOUND, messageKey.data_not_found)
      }

      if (
        vehicleModel.addOns?.length > 0 ||
        vehicleModel.fleetManagements?.length > 0
      ) {
        return failureResponse(
          code.BAD_REQUEST,
          "Cannot delete this vehicle model because it has associated addOns or vehicles",
        )
      }

      await this.vehicleModelEntityRepository.softDelete(id)

      return successMessage(messageKey.data_removed, { ":data": "city" })
    } catch (error) {
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }

  async checkVehicleModelExists(modelName: string, excludeId?: number) {
    const whereLikeCondition = {
      name: modelName,
    }

    const whereCondition: any = {}
    if (excludeId) {
      whereCondition["id"] = {
        not: excludeId,
      }
    }

    const vehicleModel = await this.vehicleModelRepository.getByParams({
      where: whereCondition,
      whereLower: whereLikeCondition,
      findOne: true,
    })

    return !isEmpty(vehicleModel)
  }
}
