import { Injectable } from "@nestjs/common"
import { ConfigService } from "@nestjs/config"

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 { ChargesTypeRepository } from "../repositories/charges-type.repository"
import { CreateChargeTypeDto } from "../dto/create-charges-type.dto"
import { ChargeType } from "../entities/charges-type.entity"
import { UpdateChargeTypeDto } from "../dto/update-charges-type.dto"

@Injectable()
export class ChargesTypeService {
  constructor(
    private chargeTypeRepository: ChargesTypeRepository,
    private readonly configService: ConfigService,
  ) {}

  async create(createChargeTypeDto: CreateChargeTypeDto) {
    if (await this.checkChargeTypeExists(createChargeTypeDto.name)) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, {
          ":data": "charge type",
        }),
      )
    }

    const chargeType = new ChargeType()
    Object.assign(chargeType, createChargeTypeDto)

    await this.chargeTypeRepository.save(chargeType)

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

  async findAll(
    limit,
    skip,
    search?: string,
    sortBy?: string,
    sortOrder?: string,
  ) {
    const validSortColumns = ["id", "name", "unitOfMeasure", "created_at"]

    let sortColumn: string
    let order: "ASC" | "DESC" = sortOrder === "ASC" ? "ASC" : "DESC"

    if (sortBy && validSortColumns.includes(sortBy)) {
      sortColumn = sortBy
      order = order || "ASC"
    } else {
      sortColumn = "created_at"
      order = "DESC"
    }

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

    const queryParams: any = {
      take: limit || defaultPagination.take,
      skip: skip || defaultPagination.skip,
      orderBy: { [sortColumn]: order },
      select: ["id", "name", "unit_of_measure", "description"],
    }

    if (search) {
      queryParams.where = {
        ...queryParams.where,
        name: { ilike: search },
      }
    }

    const chargeTypes = await this.chargeTypeRepository.getByParams(queryParams)

    if (isEmpty(chargeTypes)) {
      return failureResponse(
        code.SUCCESS,
        errorMessage(messageKey.data_not_found, {
          ":data": "charge types",
        }),
      )
    }

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, {
        ":data": "charge types",
      }),
      chargeTypes,
    )
  }

  async findOne(id: number) {
    const chargeType = await this.chargeTypeRepository.getByParams({
      where: { id },
      findOne: true,
    })

    if (isEmpty(chargeType)) {
      return failureResponse(
        code.SUCCESS,
        errorMessage(messageKey.data_not_found, {
          ":data": "charge type",
        }),
      )
    }

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, {
        ":data": "charge type",
      }),
      chargeType,
    )
  }

  async update(id: number, updateChargeTypeDto: UpdateChargeTypeDto) {
    if (await this.checkChargeTypeExists(updateChargeTypeDto.name, id)) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, {
          ":data": "charge type",
        }),
      )
    }

    const chargeType = new ChargeType()
    const condition = { id }

    Object.assign(chargeType, updateChargeTypeDto)

    const updatedChargeType = await this.chargeTypeRepository.save(
      chargeType,
      condition,
    )

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_update, { ":data": "charge type" }),
      updatedChargeType,
    )
  }

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

      if (isEmpty(chargeType)) {
        return failureResponse(
          code.DATA_NOT_FOUND,
          errorMessage(messageKey.data_not_found, {
            ":data": "charge type",
          }),
        )
      }

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

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

  async checkChargeTypeExists(name: string, excludeId?: number) {
    const whereLikeCondition = { name }
    const whereCondition: any = {}

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

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

    return !isEmpty(chargeType)
  }
}
