import { Injectable } from "@nestjs/common"
import { ConfigService } from "@nestjs/config"
import { isEmpty } from "class-validator"
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,
  successMessage,
  validationMessage,
} from "src/utils/helpers"
import { IsNull } from "typeorm"
import { ChargesTypeRepository } from "../../charges-type/repositories/charges-type.repository"
import { CityRepository } from "../../city/repositories/city.repository"
import { TripTypeRepository } from "../../trips/repositories/trip-type.repository"
import { CreateServicePricingDto } from "../dto/create-service-pricing.dto"
import { ServicePricingFilterDto } from "../dto/filter-service-pricing.dto"
import { UpdateServicePricingDto } from "../dto/update-service-pricing.dto"
import { ServicePricing } from "../entities/service-pricing.entity"
import { ServicePricingRepository } from "../repositories/service-pricing.repository"

@Injectable()
export class ServicePricingService {
  constructor(
    private readonly servicePricingRepository: ServicePricingRepository,
    private readonly chargeTypeRepository: ChargesTypeRepository,
    private readonly tripTypeRepository: TripTypeRepository,
    private readonly cityRepository: CityRepository,
    private readonly configService: ConfigService,
  ) {}

  async create(createServicePricingDto: CreateServicePricingDto) {
    const whereCondition: any = {
      service_type_id: createServicePricingDto?.service_type_id,
      charge_type_id: createServicePricingDto?.charge_type_id,
      plan_id: createServicePricingDto?.plan_id,
      city_id: createServicePricingDto?.city_id ?? IsNull(),
    }

    const isVehiclePlanExist = await this.servicePricingRepository.getByParams({
      where: whereCondition,
      findOne: true,
    })

    if (!isEmpty(isVehiclePlanExist)) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, {
          ":data": "Service plan",
        }),
      )
    }

    const isChargeTypeExist = await this.chargeTypeRepository.getByParams({
      where: {
        id: createServicePricingDto?.charge_type_id,
      },
      findOne: true,
    })

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

    const isServiceTypeExist = await this.tripTypeRepository.getByParams({
      where: {
        id: createServicePricingDto?.service_type_id,
      },
      findOne: true,
    })

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

    if (!isEmpty(createServicePricingDto?.city_id)) {
      const isCityExist = await this.cityRepository.getByParams({
        where: {
          id: createServicePricingDto?.city_id,
        },
        findOne: true,
      })

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

    const servicePricing = new ServicePricing()
    Object.assign(servicePricing, createServicePricingDto)

    await this.servicePricingRepository.save(servicePricing)

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

  async findAll(filterDto: ServicePricingFilterDto) {
    const defaultPagination = {
      take:
        filterDto?.limit ||
        this.configService.get<number>("APP.pagination.take"),
      skip:
        filterDto?.skip ||
        this.configService.get<number>("APP.pagination.skip"),
    }

    const { limit, skip, service_type_id, city_id, plan_id } = filterDto

    const queryParams: any = {
      take: Number(limit) || defaultPagination.take,
      skip: Number(skip) || defaultPagination.skip,
      orderBy: { created_at: "DESC" },
      where: {
        plan_id: plan_id,
      },
      relations: [
        "charge_type:id,name",
        "city:id,name",
        "service_type:id,name",
      ],
      select: [
        "id",
        "charge_type_id",
        "plan_id",
        "city_id",
        "price",
        "created_at",
      ],
    }
    if (service_type_id) {
      queryParams.where.service_type_id = service_type_id
    }
    if (city_id) {
      queryParams.where.city_id = city_id
    }

    const vehiclePricing =
      await this.servicePricingRepository.getByParams(queryParams)

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

  async findOne(id: number) {
    const vehiclePlan = await this.servicePricingRepository.getByParams({
      where: {
        id: id,
      },
      relations: [
        "service_type:id,name",
        "city:id,name",
        "charge_type:id,name",
      ],
      select: [
        "id",
        "plan_id",
        "charge_type_id",
        "city_id",
        "service_type_id",
        "price",
        "created_at",
      ],
      findOne: true,
    })

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

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

  async update(id: number, updateVehiclePricingDto: UpdateServicePricingDto) {
    const whereCondition: any = {
      service_type_id: updateVehiclePricingDto?.service_type_id,
      charge_type_id: updateVehiclePricingDto?.charge_type_id,
      plan_id: updateVehiclePricingDto?.plan_id,
      city_id: updateVehiclePricingDto?.city_id ?? IsNull(),
    }

    const isVehiclePlanExist = await this.servicePricingRepository.getByParams({
      where: whereCondition,
      whereNotIn: {
        id: [id],
      },
      findOne: true,
    })

    if (!isEmpty(isVehiclePlanExist)) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, {
          ":data": "Vehicle plan",
        }),
      )
    }

    const isChargeTypeExist = await this.chargeTypeRepository.getByParams({
      where: {
        id: updateVehiclePricingDto?.charge_type_id,
      },
      findOne: true,
    })

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

    const isServiceTypeExist = await this.tripTypeRepository.getByParams({
      where: {
        id: updateVehiclePricingDto?.service_type_id,
      },
      findOne: true,
    })

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

    if (!isEmpty(updateVehiclePricingDto?.city_id)) {
      const isCityExist = await this.cityRepository.getByParams({
        where: {
          id: updateVehiclePricingDto?.city_id,
        },
        findOne: true,
      })

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

    const vehiclePricing = new ServicePricing()
    Object.assign(vehiclePricing, updateVehiclePricingDto)

    await this.servicePricingRepository.save(vehiclePricing, { id: id })

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

  async remove(id: number) {
    const isVehiclePrice = await this.servicePricingRepository.getByParams({
      where: {
        id: id,
      },
      findOne: true,
    })

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

    await this.servicePricingRepository.remove({ id })

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