import { Injectable } from "@nestjs/common"
import { CreateSalarySettingDto } from "./dto/create-salary-setting.dto"
import { UpdateSalarySettingDto } from "./dto/update-salary-setting.dto"
import { SalarySettingRepository } from "./repositories/salary-setting.repository"
import {
  errorMessage,
  isEmpty,
  successMessage,
  validationMessage,
} from "../../utils/helpers"
import {
  failureResponse,
  successResponse,
} from "../../common/response/response"
import { code } from "../../common/response/response.code"
import { messageKey } from "../../constants/message-keys"
import { SalarySetting } from "./entities/salary-setting.entity"
import { verifyJwtToken } from "src/utils/jwt"

@Injectable()
export class SalarySettingsService {
  constructor(
    private readonly salarySettingRepository: SalarySettingRepository,
  ) {}

  async create(createSalarySettingDto: CreateSalarySettingDto, token: string) {
    // Check if salary setting with same type and type_value exi
    // sts for the company
    const decoded = verifyJwtToken(token)

    if (!decoded) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.invalid_token),
      )
    }

    const existingSalarySetting =
      await this.salarySettingRepository.getByParams({
        where: {
          company_id: decoded.company_id,
          type: createSalarySettingDto.type,
          type_value: createSalarySettingDto.type_value,
          name: createSalarySettingDto.name,
        },
        findOne: true,
      })

    if (existingSalarySetting) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, {
          ":data": "Salary setting",
          ":field": "type and type_value combination",
        }),
      )
    }

    let salarySetting: any = new SalarySetting()
    salarySetting = {
      ...createSalarySettingDto,
      company_id: decoded.company_id,
      created_by: decoded.user_id,
      is_taxable: createSalarySettingDto.is_taxable,
    }

    await this.salarySettingRepository.save(salarySetting)

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

  async findAll(query: any = {}, token: string) {
    const decoded = verifyJwtToken(token)

    if (!decoded) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.invalid_token),
      )
    }
    const {
      page = 1,
      limit = 10,
      search,
      type,
      column_name,
      order = "DESC",
    } = query

    const skip = (page - 1) * limit
    const take = parseInt(limit)
    const orderDirection = isEmpty(order) ? "DESC" : order.toUpperCase()

    let orderBy: any = { created_at: orderDirection }

    if (!isEmpty(column_name)) {
      switch (column_name) {
        case "name":
          orderBy = { name: orderDirection }
          break
        case "created_at":
          orderBy = { created_at: orderDirection }
          break
      }
    }

    const whereConditions: any = {}
    const searchConditions: any = {}

    // Add filters
    if (decoded.company_id) {
      whereConditions.company_id = decoded.company_id
    }

    if (type) {
      whereConditions.type = type
    }

    // Add search functionality
    if (search) {
      searchConditions.name = search
    }

    const salarySettings: any = await this.salarySettingRepository.getByParams({
      where: whereConditions,
      search: !isEmpty(searchConditions) ? searchConditions : undefined,
      whereNull: ["deleted_at"],
      relations: ["company:id,name"],
      orderBy,
      take,
      skip,
    })

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

  async findOne(id: number) {
    const salarySetting: any = await this.salarySettingRepository.getByParams({
      where: { id },
      relations: ["company:id,name"],
      findOne: true,
    })

    if (isEmpty(salarySetting)) {
      return failureResponse(
        code.VALIDATION,
        errorMessage(messageKey.data_not_found, { ":data": "Salary setting" }),
      )
    }

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

  async update(
    id: number,
    updateSalarySettingDto: UpdateSalarySettingDto,
    token: string,
  ) {
    const decoded = verifyJwtToken(token)

    if (!decoded) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.invalid_token),
      )
    }

    const salarySetting: any = await this.salarySettingRepository.getByParams({
      where: { id, company_id: decoded.company_id },
      findOne: true,
    })

    if (isEmpty(salarySetting)) {
      return failureResponse(
        code.VALIDATION,
        errorMessage(messageKey.data_not_found, { ":data": "Salary setting" }),
      )
    }

    // Check if salary setting with same type and type_value exists for the company (excluding current record)
    if (updateSalarySettingDto.type || updateSalarySettingDto.type_value) {
      const existingSalarySetting =
        await this.salarySettingRepository.getByParams({
          where: {
            company_id: decoded.company_id,
            type: updateSalarySettingDto.type || salarySetting.type,
            type_value:
              updateSalarySettingDto.type_value || salarySetting.type_value,
            name: updateSalarySettingDto.name || salarySetting.name,
          },
          whereNotIn: { id: [id] },
          findOne: true,
        })

      if (existingSalarySetting) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.already_exist, {
            ":data": "Salary setting",
            ":field": "type and type_value combination",
          }),
        )
      }
    }

    // Update the salary setting
    Object.assign(salarySetting, updateSalarySettingDto)
    salarySetting.updated_by = decoded.user_id
    salarySetting.is_taxable = updateSalarySettingDto.is_taxable

    await this.salarySettingRepository.save(salarySetting)

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

  async remove(id: number, token: string) {
    const decoded = verifyJwtToken(token)

    if (!decoded) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.invalid_token),
      )
    }
    const companyId = decoded.company_id

    const salarySetting: any = await this.salarySettingRepository.getByParams({
      where: { id, company_id: companyId },
      findOne: true,
    })

    if (isEmpty(salarySetting)) {
      return failureResponse(
        code.VALIDATION,
        errorMessage(messageKey.data_not_found, { ":data": "Salary setting" }),
      )
    }

    // Check if this salary setting can be deleted (not a protected code)
    // if (salarySetting.code && !canDeleteSalarySetting(salarySetting.code)) {
    //   const protectedCodes = getProtectedSalaryCodes().join(", ")
    //   return failureResponse(
    //     code.VALIDATION,
    //     `Cannot delete salary setting with code '${salarySetting.code}'. Protected salary codes (${protectedCodes}) cannot be deleted as they are required for TDS calculations.`,
    //   )
    // }

    // Soft delete
    await this.salarySettingRepository.remove({ id: salarySetting.id })

    await this.salarySettingRepository.save(
      {
        id: salarySetting.id,
        deleted_by: decoded.user_id,
      },
      { id: salarySetting.id },
    )

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

  async checkSalarySettingExist(
    type: string,
    typeValue: string,
    companyId: number,
  ) {
    const salarySetting = await this.salarySettingRepository.getByParams({
      where: {
        type: type,
        type_value: typeValue,
        company_id: companyId,
      },
      findOne: true,
    })

    return !isEmpty(salarySetting)
  }
}
