import { Injectable } from "@nestjs/common"
import { CreatePlanDto } from "../dto/create-plan.dto"
import { UpdatePlanDto } from "../dto/update-plan.dto"
import { PricingPlanRepository } from "../repositories/plan.repository"
import {
  errorMessage,
  isEmpty,
  successMessage,
  validationMessage,
} from "src/utils/helpers"
import { Plan } from "../entities/plan.entity"
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 "../../charges-type/repositories/charges-type.repository"

import { ClientCompanyContractRepository } from "../../client-contract/repositories/client-contarct.repository"

@Injectable()
export class PlansService {
  constructor(
    private readonly pricingPlanRepository: PricingPlanRepository,
    private readonly chargeTypeRepository: ChargesTypeRepository,
    private readonly clientContractRepository: ClientCompanyContractRepository,
  ) {}

  async create(createPlanDto: CreatePlanDto) {
    const isPlanExisting = await this.pricingPlanRepository.getByParams({
      whereLower: {
        name: createPlanDto?.name,
      },
      findOne: true,
    })

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

    const isChargeTypeExist = await this.chargeTypeRepository.getByParams({
      where: {
        id: createPlanDto?.charge_type_id,
      },
      findOne: true,
    })

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

    if (createPlanDto?.is_default) {
      const defaultPlanExist: any =
        await this.pricingPlanRepository.getByParams({
          where: {
            is_default: true,
          },
          findOne: true,
        })

      if (!isEmpty(defaultPlanExist)) {
        await this.pricingPlanRepository.save(
          { is_default: false },
          { id: defaultPlanExist?.id },
        )
      }
    }

    const pricingPlan = new Plan()

    Object.assign(pricingPlan, createPlanDto)

    await this.pricingPlanRepository.save(pricingPlan)

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

  async findAll(search: any) {
    const queryParams: any = {
      orderBy: { created_at: "DESC" },
      select: [
        "id",
        "name",
        "description",
        "is_default",
        "created_at",
        "charge_type_id",
      ],
      relations: ["charge_type"],
    }

    if (!isEmpty(search)) {
      queryParams.search = {
        name: search,
        description: search,
      }
    }

    const pricingPlans: any =
      await this.pricingPlanRepository.getByParams(queryParams)

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, { ":data": "Plan" }),
      pricingPlans,
    )
  }

  async findOne(id: number) {
    const pricingPlan = await this.pricingPlanRepository.getByParams({
      where: {
        id: id,
      },
      select: [
        "id",
        "name",
        "description",
        "is_default",
        "created_at",
        "charge_type_id",
      ],
      relations: ["charge_type"],
      findOne: true,
    })

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

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

  async update(id: number, updatePlanDto: UpdatePlanDto) {
    const isPlanExisting: any = await this.pricingPlanRepository.getByParams({
      where: {
        id: id,
      },
      findOne: true,
    })

    if (isEmpty(isPlanExisting)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, {
          ":data": "Plan ",
        }),
      )
    }

    const PlanExisting = await this.pricingPlanRepository.getByParams({
      whereLower: {
        name: updatePlanDto?.name,
      },
      whereNotIn: {
        id: [id],
      },
    })

    if (!isEmpty(PlanExisting)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        validationMessage(messageKey.already_exist, {
          ":data": "plan",
        }),
      )
    }

    if (updatePlanDto?.is_default) {
      const defaultPlanExist: any =
        await this.pricingPlanRepository.getByParams({
          where: {
            is_default: true,
          },
          findOne: true,
        })

      if (!isEmpty(defaultPlanExist)) {
        await this.pricingPlanRepository.save(
          { is_default: false },
          { id: defaultPlanExist?.id },
        )
      }
    }

    const isChargeTypeExist = await this.chargeTypeRepository.getByParams({
      where: {
        id: updatePlanDto?.charge_type_id,
      },
      findOne: true,
    })

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

    Object.assign(isPlanExisting, updatePlanDto)

    await this.pricingPlanRepository.save(isPlanExisting, { id: id })

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

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

    if (isEmpty(isPlanExisting)) {
      return failureResponse(
        code.DATA_NOT_FOUND,
        errorMessage(messageKey.data_not_found, {
          ":data": "Plan ",
        }),
      )
    }

    const isPlanAssigned = await this.clientContractRepository.getByParams({
      where: {
        payment_plan_id: id,
      },
      findOne: true,
    })

    if (!isEmpty(isPlanAssigned)) {
      return failureResponse(code.BAD_REQUEST, messageKey.plan_already_in_use)
    }

    await this.pricingPlanRepository.remove({ id })

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