import { Injectable } from "@nestjs/common"
import { ConfigService } from "@nestjs/config"
import { IsNull, Repository } from "typeorm"
import { AddOnRepository } from "../../add-ons/repositories/add-ons.repository"
import { CityRepository } from "../../city/repositories/city.repository"
import { VehicleRepository } from "../../vehicle-type/repositories/vehicle-type.repository"
import { AddOnsPricing } from "../entities/add-ons-pricing.entity"
import { AddOnsPricingRepository } from "../repositories/add-ons-pricing.repository"
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,
  validationMessage,
} from "src/utils/helpers"
import { PricingPlanRepository } from "../../plans/repositories/plan.repository"
import { CreateAddOnPricingDto } from ".././dto/create-add-ons-pricing.dto"
import { AddOnsPricingFilterDto } from ".././dto/filter-add-ons-pricing.dto"
import { UpdateAddOnPricingDto } from ".././dto/update-add-ons-pricing.dto"
import { InjectRepository } from "@nestjs/typeorm"

@Injectable()
export class AddOnsPricingService {
  constructor(
    private readonly addOnsPricingRepository: AddOnsPricingRepository,
    private readonly vehicleTypeRepository: VehicleRepository,
    private readonly cityRepository: CityRepository,
    private readonly planRepository: PricingPlanRepository,
    private readonly addOnRepository: AddOnRepository,
    private readonly configService: ConfigService,
    @InjectRepository(AddOnsPricing)
    private readonly addOnsPricingEntityRepository: Repository<AddOnsPricing>,
  ) {}

  async create(createDto: CreateAddOnPricingDto) {
    const whereCondition: any = {
      plan_id: createDto?.plan_id,
      vehicle_type_id: createDto?.vehicle_type_id ?? IsNull(),
      add_on_id: createDto?.add_on_id,
      city_id: createDto?.city_id ?? IsNull(),
    }

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

    if (!isEmpty(isExist)) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, {
          ":data": "Add-on pricing",
        }),
      )
    }

    // Check Plan
    const isPlanExist = await this.planRepository.getByParams({
      where: { id: createDto?.plan_id },
      findOne: true,
    })

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

    // Check Vehicle Type
    if (!isEmpty(createDto?.vehicle_type_id)) {
      const isVehicleTypeExist = await this.vehicleTypeRepository.getByParams({
        where: { id: createDto?.vehicle_type_id },
        findOne: true,
      })

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

    // Check AddOn
    const isAddOnExist = await this.addOnRepository.getByParams({
      where: { id: createDto?.add_on_id },
      findOne: true,
    })
    if (isEmpty(isAddOnExist)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, { ":data": "Add-On" }),
      )
    }

    // Check City (if provided)
    if (!isEmpty(createDto?.city_id)) {
      const isCityExist = await this.cityRepository.getByParams({
        where: { id: createDto?.city_id },
        findOne: true,
      })
      if (isEmpty(isCityExist)) {
        return failureResponse(
          code.DATA_NOT_FOUND,
          errorMessage(messageKey.data_not_found, { ":data": "City" }),
        )
      }
    }

    const addOnsPricing = new AddOnsPricing()
    Object.assign(addOnsPricing, createDto)

    await this.addOnsPricingRepository.save(addOnsPricing)

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

  async findAll(planId: number, filterDto: AddOnsPricingFilterDto) {
    const { search, vehicle_type_id, add_on_id, city_id } = filterDto

    const qb = this.addOnsPricingEntityRepository
      .createQueryBuilder("a")
      .select("add_on.id", "add_on_id")
      .addSelect("add_on.name", "add_on_name")
      .addSelect(
        `json_agg(
        json_build_object(
          'id', a.id,
          'price', a.price,
          'vehicle_type', json_build_object('id', vehicle_type.id, 'name', vehicle_type.name),
          'plan', json_build_object('id', plan.id, 'name', plan.name),
          'city', json_build_object('id', city.id, 'name', city.name),
          'created_at', a.created_at
        )
      )`,
        "items",
      )
      .leftJoin("a.add_on", "add_on")
      .leftJoin("a.plan", "plan")
      .leftJoin("a.vehicle_type", "vehicle_type")
      .leftJoin("a.city", "city")
      .where("a.plan_id = :planId", { planId })

    if (vehicle_type_id) {
      qb.andWhere("a.vehicle_type_id = :vehicle_type_id", { vehicle_type_id })
    }
    if (add_on_id) {
      qb.andWhere("a.add_on_id = :add_on_id", { add_on_id })
    }
    if (city_id) {
      qb.andWhere("a.city_id = :city_id", { city_id })
    }
    if (search) {
      qb.andWhere(
        "(vehicle_type.name ILIKE :search OR add_on.name ILIKE :search)",
        { search: `%${search}%` },
      )
    }

    qb.groupBy("add_on.id").addGroupBy("add_on.name")

    const addOnsPricing = await qb.getRawMany()

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, { ":data": "Add-on Pricing" }),
      addOnsPricing,
    )
  }

  async findOne(id: number) {
    const addOnPricing = await this.addOnsPricingRepository.getByParams({
      where: { id },
      relations: [
        "plan:id,name",
        "vehicle_type:id,name",
        "add_on:id,name",
        "city:id,name",
        "city.state:id,name",
        "city.country:id,name",
      ],
      findOne: true,
    })

    if (isEmpty(addOnPricing)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, { ":data": "Add-on Pricing" }),
      )
    }

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, { ":data": "Add-on Pricing" }),
      addOnPricing,
    )
  }

  async update(id: number, updateDto: UpdateAddOnPricingDto) {
    const whereCondition: any = {
      plan_id: updateDto?.plan_id,
      vehicle_type_id: updateDto?.vehicle_type_id ?? IsNull(),
      add_on_id: updateDto?.add_on_id,
      city_id: updateDto?.city_id ?? IsNull(),
    }

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

    if (!isEmpty(isExist)) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, {
          ":data": "Add-on Pricing",
        }),
      )
    }

    // Check Plan
    const isPlanExist = await this.planRepository.getByParams({
      where: { id: updateDto?.plan_id },
      findOne: true,
    })
    if (isEmpty(isPlanExist)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, { ":data": "Plan" }),
      )
    }

    // Check Vehicle
    if (!isEmpty(updateDto?.vehicle_type_id)) {
      const isVehicleTypeExist = await this.vehicleTypeRepository.getByParams({
        where: { id: updateDto?.vehicle_type_id },
        findOne: true,
      })

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

    // Check AddOn
    const isAddOnExist = await this.addOnRepository.getByParams({
      where: { id: updateDto?.add_on_id },
      findOne: true,
    })

    if (isEmpty(isAddOnExist)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, { ":data": "Add-On" }),
      )
    }

    // Check City (if provided)
    if (!isEmpty(updateDto?.city_id)) {
      const isCityExist = await this.cityRepository.getByParams({
        where: { id: updateDto?.city_id },
        findOne: true,
      })

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

    const addOnsPricing = new AddOnsPricing()

    Object.assign(addOnsPricing, updateDto)

    await this.addOnsPricingRepository.save(addOnsPricing, { id })

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

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

    if (isEmpty(isExist)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, { ":data": "Add-on Pricing" }),
      )
    }

    await this.addOnsPricingRepository.remove({ id })

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_removed, { ":data": "Add-on Pricing" }),
    )
  }
}
