import { Injectable } from "@nestjs/common"
import { CreateVehicleManufacturerDto } from "../dto/create-vehicle-manufacturer.dto"
import { UpdateVehicleManufacturerDto } from "../dto/update-vehicle-manufacturer.dto"
import { VehicleManufacturer } from "../entities/vehicle-manufacturer.entity"
import { VehicleManufacturerRepository } from "../repositories/vehicle-manufacturer.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 { InjectRepository } from "@nestjs/typeorm"
import { Repository } from "typeorm"

@Injectable()
export class VehicleManufacturerService {
  constructor(
    private vehicleManufacturerRepository: VehicleManufacturerRepository,
    private readonly configService: ConfigService,
    @InjectRepository(VehicleManufacturer)
    private readonly vehicleManufacturerModelRepository: Repository<VehicleManufacturer>,
  ) {}

  async create(createVehicleManufacturerDto: CreateVehicleManufacturerDto) {
    if (
      await this.checkVehicleManufacturerExists(
        createVehicleManufacturerDto.name,
      )
    ) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, {
          ":data": "vehicle manufacturer",
        }),
      )
    }

    const vehicleManufacturer = new VehicleManufacturer()

    Object.assign(vehicleManufacturer, createVehicleManufacturerDto)

    await this.vehicleManufacturerRepository.save(vehicleManufacturer)

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

  async findAll(
    limit,
    skip,
    search?: string,
    sortBy?: string,
    sortOrder?: string,
  ) {
    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,
      orderBy: { [sortColumn]: order },
      select: ["id", "name", "status"],
    }

    if (search) {
      queryParams.where = {
        ...queryParams.where,
        name: { ilike: search },
      }
    }

    const manufacturer =
      await this.vehicleManufacturerRepository.getByParams(queryParams)

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

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

  async findOne(id: number) {
    const manufacturer = await this.vehicleManufacturerRepository.getByParams({
      where: { id },
      findOne: true,
    })

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

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

  async update(
    id: number,
    updateVehicleManufacturerDto: UpdateVehicleManufacturerDto,
  ) {
    if (
      await this.checkVehicleManufacturerExists(
        updateVehicleManufacturerDto.name,
        id,
      )
    ) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, {
          ":data": "vehicle manufacturer",
        }),
      )
    }

    const manufacturer = new VehicleManufacturer()

    const condition = {
      id: id,
    }

    Object.assign(manufacturer, updateVehicleManufacturerDto)

    const vehicleManufacturer = await this.vehicleManufacturerRepository.save(
      manufacturer,
      condition,
    )

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

  async remove(id: number) {
    try {
      const manufacturer: any =
        await this.vehicleManufacturerRepository.getByParams({
          where: { id },
          findOne: true,
          relations: ["vehicle_models"],
        })

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

      if (manufacturer.vehicle_models?.length > 0) {
        return failureResponse(
          code.BAD_REQUEST,
          "Cannot delete this vehicle manufacturer because it has associated vehicle models.",
        )
      }

      await this.vehicleManufacturerModelRepository.softDelete(id)

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

  async checkVehicleManufacturerExists(
    manufacturerName: string,
    excludeId?: number,
  ) {
    const whereLikeCondition = {
      name: manufacturerName,
    }

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

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

    return !isEmpty(manufacturer)
  }

  async getVehicleManufacturerWiseVehicles(id: number) {
    try {
      const vehicleManufacturer =
        await this.vehicleManufacturerRepository.getByParams({
          where: { id },
          relations: ["vehicle_models:id,name"],
          select: ["id", "name"],
        })

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, {
          ":data": "vehicle manufacturer",
        }),
        vehicleManufacturer,
      )
    } catch (error) {
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }
}
