import { Injectable } from "@nestjs/common"
import { CurrencyRepository } from "../repositories/currency.repository"
import { CreateCurrencyDto } from "../dto/create-currency.dto"
import { isEmpty } from "src/utils/helpers"
import { failureResponse, successResponse } from "src/common/response/response"
import { messageKey } from "src/constants/message-keys"
import { FindAllCurrencyDto } from "../dto/find-all-currency.dto"
import { Currency } from "../entities/currency.entity"
import { UpdateCurrencyDto } from "../dto/update-currency.dto"

@Injectable()
export class CurrencyService {
  constructor(private readonly currencyRepository: CurrencyRepository) {}

  async checkCurrencyExist(
    currency_code: string,
    name: string,
    excludeId?: number,
  ): Promise<boolean> {
    const whereCondition: any = {}
    const wherelowerCondition: any = {
      currency_code,
      name,
    }

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

    const currency = await this.currencyRepository.getByParams({
      where: whereCondition,
      whereLower: wherelowerCondition,
      findOne: true,
    })

    return !isEmpty(currency)
  }

  async createCurrency(createCurrencyDto: CreateCurrencyDto) {
    if (createCurrencyDto.currency_code.length > 3) {
      return failureResponse(400, messageKey.invalid_code_length)
    }

    const existingCurrency = await this.checkCurrencyExist(
      createCurrencyDto.currency_code,
      createCurrencyDto.name,
    )

    if (existingCurrency) {
      return failureResponse(409, messageKey.already_exist)
    }

    try {
      const result = await this.currencyRepository.save({
        currency_code: createCurrencyDto.currency_code,
        country_id: createCurrencyDto.country_id,
        name: createCurrencyDto.name,
        symbol: createCurrencyDto.symbol,
      })

      return result
    } catch (error) {
      return failureResponse(500, messageKey.something_went_wrong)
    }
  }

  async findAllCurrency(params: FindAllCurrencyDto) {
    const validSortColumns = [
      "id",
      "currency_code",
      "name",
      "symbol",
      "created_at",
      "updated_at",
      "country.name",
    ]

    let sortColumn: string
    let sortOrder: "ASC" | "DESC"

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

    const filters: any = {}

    if (params?.country_id) {
      filters["country_id"] = params.country_id.split(",")
    }

    const currency = await this.currencyRepository.getByParams({
      search: {
        currency_code: params.search,
      },
      whereIn: filters,
      take: Number(params.limit),
      skip: Number(params.skip),
      orderBy: {
        [sortColumn]: sortOrder,
      },
      relations: ["country:id,name"],
    })

    return {
      success: true,
      code: 200,
      message: messageKey.data_retrieve,
      data: currency,
    }
  }

  async findOneCurrency(id: number): Promise<Currency> {
    const currency = (await this.currencyRepository.getByParams({
      where: { id },
      findOne: true,
    })) as Currency

    if (!currency) {
      throw new Error(messageKey.data_not_found)
    }

    return currency
  }

  async updateCurrency(id: number, updateCurrencyDto: UpdateCurrencyDto) {
    try {
      const currency = (await this.currencyRepository.getByParams({
        where: { id },
        findOne: true,
      })) as Currency

      if (!currency) {
        return failureResponse(404, messageKey.data_not_found)
      }

      const existingCurrency = await this.checkCurrencyExist(
        updateCurrencyDto.currency_code,
        updateCurrencyDto.name,
        id,
      )

      if (existingCurrency) {
        return failureResponse(400, messageKey.already_exist)
      }

      Object.assign(currency, updateCurrencyDto)

      const updatedCurrency = await this.currencyRepository.save(currency)

      return successResponse(200, messageKey.data_update, updatedCurrency)
    } catch (error) {
      return failureResponse(500, messageKey.something_went_wrong)
    }
  }

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

    if (isEmpty(currency)) {
      return failureResponse(404, messageKey.data_not_found)
    }

    await this.currencyRepository.remove({ id })

    return { success: true, code: 200, message: messageKey.data_removed }
  }
}
