import { Injectable } from "@nestjs/common"
import { CreateVehiclePricingDto } from "./dto/create-vehicle-pricing.dto"
import { UpdateVehiclePricingDto } from "./dto/update-vehicle-pricing.dto"
import { VehiclePricingRepository } from "./repositories/vehicle-pricing.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 { VehicleRepository } from "../vehicle-type/repositories/vehicle-type.repository"
import { CityRepository } from "../city/repositories/city.repository"
import { TripTypeRepository } from "../trips/repositories/trip-type.repository"
import { VehiclePricing } from "./entities/vehicle-pricing.entity"
import { IsNull, Repository } from "typeorm"
import { VehiclePricingFilterDto } from "./dto/filter-vehicle-pricing.dto"
import { ConfigService } from "@nestjs/config"
import { InjectRepository } from "@nestjs/typeorm"

@Injectable()
export class VehiclePricingService {
  constructor(
    private readonly vehiclePricingRepository: VehiclePricingRepository,
    private readonly vehicleTypeRepository: VehicleRepository,
    private readonly cityRepository: CityRepository,
    private readonly tripTypeRepository: TripTypeRepository,
    private readonly configService: ConfigService,

    @InjectRepository(VehiclePricing)
    private readonly vehiclePricingEntityRepository: Repository<VehiclePricing>,
  ) {}

  async create(createVehiclePricingDto: CreateVehiclePricingDto) {
    const whereCondition: any = {
      vehicle_type_id: createVehiclePricingDto?.vehicle_type_id,
      service_type_id: createVehiclePricingDto?.service_type_id,
      plan_id: createVehiclePricingDto?.plan_id,
      city_id:
        createVehiclePricingDto?.city_id != null
          ? createVehiclePricingDto.city_id
          : IsNull(),
    }

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

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

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

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

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

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

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

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

    const vehiclePricing = new VehiclePricing()
    Object.assign(vehiclePricing, createVehiclePricingDto)

    await this.vehiclePricingRepository.save(vehiclePricing)

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

  async findAll(id: number, filterDto: VehiclePricingFilterDto) {
    const { search, vehicle_type_id, service_type_id, city_id } = filterDto

    const query = this.vehiclePricingEntityRepository
      .createQueryBuilder("vp")
      .leftJoin("vp.vehicle_type", "vehicle_type")
      .leftJoin("vp.service_type", "service_type")
      .leftJoin("vp.city", "city")
      .where("vp.plan_id = :id", { id })

    if (search) {
      query.andWhere(
        "(vehicle_type.name ILIKE :search OR service_type.name ILIKE :search OR city.name ILIKE :search)",
        { search: `%${search}%` },
      )
    }

    if (vehicle_type_id) {
      query.andWhere("vp.vehicle_type_id = :vehicle_type_id", {
        vehicle_type_id,
      })
    }

    if (service_type_id) {
      query.andWhere("vp.service_type_id = :service_type_id", {
        service_type_id,
      })
    }

    if (city_id) {
      query.andWhere("vp.city_id = :city_id", { city_id })
    }

    // ✅ Group by vehicle_type and aggregate records
    query
      .select([
        "vp.vehicle_type_id AS vehicle_type_id",
        "vehicle_type.name AS vehicle_type_name",
        `json_agg(
        json_build_object(
          'id', vp.id,
          'plan_id', vp.plan_id,
          'service_type_id', vp.service_type_id,
          'city_id', vp.city_id,
          'price', vp.price,
          'created_at', vp.created_at,
          'updated_at', vp.updated_at,
          'deleted_at', vp.deleted_at,
          'service_type', json_build_object('id', service_type.id, 'name', service_type.name),
          'city', json_build_object('id', city.id, 'name', city.name)
        ) ORDER BY vp.created_at DESC
      ) AS data`,
      ])
      .groupBy("vp.vehicle_type_id")
      .addGroupBy("vehicle_type.name")

    const result = await query.getRawMany()

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

  async findOne(id: number) {
    const vehiclePlan = await this.vehiclePricingRepository.getByParams({
      where: {
        id: id,
      },
      relations: [
        "vehicle_type:id,name",
        "service_type:id,name",
        "city:id,name",
      ],
      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: UpdateVehiclePricingDto) {
    const whereCondition: any = {
      vehicle_type_id: updateVehiclePricingDto?.vehicle_type_id,
      service_type_id: updateVehiclePricingDto?.service_type_id,
      plan_id: updateVehiclePricingDto?.plan_id,
      city_id:
        updateVehiclePricingDto?.city_id != null
          ? updateVehiclePricingDto.city_id
          : IsNull(),
    }

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

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

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

    if (isEmpty(isVehicleTypeExist)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, { ":data": "Vehicle 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": "Service Type" }),
        )
      }
    }

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

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

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

  async remove(id: number) {
    const isVehiclePrice = await this.vehiclePricingRepository.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.vehiclePricingRepository.remove({ id })

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