import { Injectable } from "@nestjs/common"
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 { AddOnRepository } from "../repositories/add-ons.repository"
import { CreateAddOnDto } from "../dto/create-add-ons.dto"
import { AddOn } from "../entities/add-ons.entity"
import { UpdateAddOnDto } from "../dto/update-add-ons.dto"
import { In, Repository } from "typeorm"
import { InjectRepository } from "@nestjs/typeorm"
import { VehicleModel } from "../../vehicle-model/entities/vehicle-model.entity"
import { TripRepository } from "../../trips/repositories/trip.repository"
import { PricingPlanRepository } from "../../plans/repositories/plan.repository"

@Injectable()
export class AddOnService {
  constructor(
    private readonly addOnRepository: AddOnRepository,
    private readonly configService: ConfigService,
    private readonly tripRepository: TripRepository,
    private readonly pricingPlanRepository: PricingPlanRepository,
    @InjectRepository(AddOn)
    private readonly addOnsEntityRepository: Repository<AddOn>,
    @InjectRepository(VehicleModel)
    private readonly vehicleModelRepository: Repository<VehicleModel>,
  ) {}

  async create(createAddOnDto: CreateAddOnDto) {
    try {
      if (await this.checkAddOnExists(createAddOnDto.name)) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.already_exist, {
            ":data": "add-on",
          }),
        )
      }

      const addOn = new AddOn()
      Object.assign(addOn, createAddOnDto) as AddOn

      if (createAddOnDto.vehicle_model_ids?.length > 0) {
        addOn.vehicleModels = await this.vehicleModelRepository.findBy({
          id: In(createAddOnDto.vehicle_model_ids),
        })
      }

      await this.addOnRepository.save(addOn)

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_add, {
          ":data": "add-on",
        }),
      )
    } catch (error) {
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }

  async findAll(
    limit?: string,
    skip?: string,
    search?: string,
    sortBy?: string,
    sortOrder?: string,
    vehicle_model_ids: number[] = [],
  ) {
    try {
      const validSortColumns = [
        "id",
        "name",
        "created_at",
        "updated_at",
        "is_editable",
      ]
      const order: "ASC" | "DESC" = sortOrder === "ASC" ? "ASC" : "DESC"
      const sortColumn = validSortColumns.includes(sortBy)
        ? sortBy
        : "created_at"

      const defaultPagination = {
        take: Number(this.configService.get("APP.pagination.take")),
        skip: Number(this.configService.get("APP.pagination.skip")),
      }
      const take = limit ? Number(limit) : defaultPagination.take
      const offset = skip ? Number(skip) : defaultPagination.skip

      const filterIds = Array.isArray(vehicle_model_ids)
        ? vehicle_model_ids
        : [Number(vehicle_model_ids)]

      // This function must return an object like: { count, data }
      const data: any = await this.addOnRepository.findWithVehicleModelFilter(
        take,
        offset,
        search,
        sortColumn,
        order,
        filterIds,
      )

      if (!data || data.length === 0) {
        return failureResponse(
          code.SUCCESS,
          errorMessage(messageKey.data_not_found, {
            ":data": "Add-on",
          }),
        )
      }

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, {
          ":data": "add-on",
        }),
        {
          count: data.count,
          data: data.data,
        },
      )
    } catch (error) {
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }

  async getAddOnsForTrip(
    tripId: number,
    search: string,
    skip: number,
    limit: number,
  ) {
    const trip: any = await this.tripRepository.getByParams({
      where: { id: tripId },
      select: ["id", "client_id", "plan_id", "city_id"],
      findOne: true,
    })

    const query = this.addOnsEntityRepository
      .createQueryBuilder("addon")
      .innerJoin(
        "add_ons_pricing",
        "add_ons_pricing",
        "add_ons_pricing.add_on_id = addon.id",
      )
      .where("add_ons_pricing.plan_id = :planId", { planId: trip.plan_id })
      .andWhere(
        "(add_ons_pricing.city_id = :cityId OR add_ons_pricing.city_id IS NULL)",
        { cityId: trip.city_id },
      )
      .andWhere("addon.is_editable = :isEditable", { isEditable: true })

    if (search) {
      query.andWhere(
        "(addon.name ILIKE :search OR addon.description ILIKE :search)",
        { search: `%${search}%` },
      )
    }

    query.skip(skip).take(limit)

    const [data, total] = await query.getManyAndCount()

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, { ":data": "add-on" }),
      { count: total, data },
    )
  }

  async findOne(id: number) {
    try {
      const addOn = await this.addOnRepository.getByParams({
        where: { id },
        findOne: true,
        relations: ["vehicleModels:id,name"],
      })

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

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, {
          ":data": "add-on",
        }),
        addOn,
      )
    } catch (error) {
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }

  async update(id: number, updateAddOnDto: UpdateAddOnDto) {
    try {
      if (await this.checkAddOnExists(updateAddOnDto.name, id)) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.already_exist, {
            ":data": "add-on",
          }),
        )
      }

      const addOn = (await this.addOnRepository.getByParams({
        where: { id },
        relations: ["vehicleModels"],
        findOne: true,
      })) as AddOn

      if (!addOn) {
        return failureResponse(code.DATA_NOT_FOUND, "Add-on not found")
      }

      addOn.name = updateAddOnDto.name
      addOn.description = updateAddOnDto.description

      if (updateAddOnDto.vehicle_model_ids?.length > 0) {
        addOn.vehicleModels = await this.vehicleModelRepository.findBy({
          id: In(updateAddOnDto.vehicle_model_ids),
        })
      } else {
        addOn.vehicleModels = []
      }

      const updated = await this.addOnRepository.save(addOn)

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_update, {
          ":data": "add-on",
        }),
        updated,
      )
    } catch (error) {
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }

  async remove(id: number) {
    try {
      const addOn = await this.addOnRepository.getByParams({
        where: { id },
        findOne: true,
      })

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

      await this.addOnRepository.remove({ id }, null, false)

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

  async getAddOnsDropdown() {
    const addOns = await this.addOnRepository.getByParams({
      where: {},
      select: ["id", "name"],
      orderBy: { name: "ASC" },
      relations: ["vehicleModels:id.vehicle_type:id,name"],
    })

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

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, { ":data": "Add-ons" }),
      addOns,
    )
  }

  async checkAddOnExists(name: string, excludeId?: number) {
    try {
      const whereLikeCondition = { name }

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

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

      return !isEmpty(addOn)
    } catch (error) {
      return failureResponse(code.ERROR, messageKey.something_went_wrong)
    }
  }
}
