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 { 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 queryBuilder = this.vehiclePricingEntityRepository
      .createQueryBuilder("vp")
      .where("vp.vehicle_type_id = :vehicle_type_id", {
        vehicle_type_id: createVehiclePricingDto?.vehicle_type_id,
      })
      .andWhere("vp.service_type_id = :service_type_id", {
        service_type_id: createVehiclePricingDto?.service_type_id,
      })
      .andWhere("vp.plan_id = :plan_id", {
        plan_id: createVehiclePricingDto?.plan_id,
      })

    // ✅ Key fix: handle NULL city_id explicitly
    if (createVehiclePricingDto?.city_id != null) {
      queryBuilder.andWhere("vp.city_id = :city_id", {
        city_id: createVehiclePricingDto.city_id,
      })
    } else {
      queryBuilder.andWhere("vp.city_id IS NULL") // ✅ Explicit IS NULL check
    }

    const isVehiclePlanExist = await queryBuilder.getOne()

    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")
      .leftJoin("city.state", "state")
      .leftJoin("city.country", "country")
      .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,
            'state', json_build_object('id', state.id, 'name', state.name),
            'country', json_build_object('id', country.id, 'name', country.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",
        "city.state:id,name",
        "city.country: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,
    // })

    // Manually build query to handle NULL city_id + exclude current id
    const queryBuilder = this.vehiclePricingEntityRepository
      .createQueryBuilder("vp")
      .where("vp.vehicle_type_id = :vehicle_type_id", {
        vehicle_type_id: updateVehiclePricingDto?.vehicle_type_id,
      })
      .andWhere("vp.service_type_id = :service_type_id", {
        service_type_id: updateVehiclePricingDto?.service_type_id,
      })
      .andWhere("vp.plan_id = :plan_id", {
        plan_id: updateVehiclePricingDto?.plan_id,
      })
      .andWhere("vp.id != :id", { id }) // Exclude current record

    // ✅ Key fix: handle NULL city_id explicitly
    if (updateVehiclePricingDto?.city_id != null) {
      queryBuilder.andWhere("vp.city_id = :city_id", {
        city_id: updateVehiclePricingDto.city_id,
      })
    } else {
      queryBuilder.andWhere("vp.city_id IS NULL") // ✅ Explicit IS NULL check
    }

    const isVehiclePlanExist = await queryBuilder.getOne()

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

    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" }),
    )
  }
}
