import { Injectable } from "@nestjs/common"
import { CreateSalaryStructureDto } from "./dto/create-salary-structure.dto"
import { UpdateSalaryStructureDto } from "./dto/update-salary-structure.dto"
import { SalaryStructureRepository } from "./repositories/salary-structure.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 { SalaryStructure } from "./entities/salary-structure.entity"
import { verifyJwtToken } from "src/utils/jwt"

@Injectable()
export class SalaryStructureService {
  constructor(
    private readonly salaryStructureRepository: SalaryStructureRepository,
  ) {}

  async create(
    createSalaryStructureDto: CreateSalaryStructureDto,
    token: string,
  ) {
    const decoded = verifyJwtToken(token)
    if (!decoded) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.invalid_token),
      )
    }

    // Check if there's an active salary structure for this employee
    const activeSalaryStructure: any =
      await this.salaryStructureRepository.getByParams({
        where: {
          company_id: decoded.company_id,
          employee_id: createSalaryStructureDto.employee_id,
          status: 1,
        },
        whereNull: ["effective_to"],
        findOne: true,
      })

    // If creating a new active structure, deactivate the current one
    if (
      activeSalaryStructure &&
      (!createSalaryStructureDto.effective_to ||
        createSalaryStructureDto.status === 1)
    ) {
      // Set effective_to date for the current active structure
      const effectiveFromDate = new Date(
        createSalaryStructureDto.effective_from,
      )
      const previousDay = new Date(effectiveFromDate)
      previousDay.setDate(previousDay.getDate() - 1)

      activeSalaryStructure.effective_to = previousDay
      activeSalaryStructure.updated_by = decoded.user_id
      await this.salaryStructureRepository.save(activeSalaryStructure)
    }

    let salaryStructure: any = new SalaryStructure()
    salaryStructure = {
      ...createSalaryStructureDto,
      status: createSalaryStructureDto.status || 1,
      company_id: decoded.company_id,
      created_by: decoded.user_id,
      effective_from: new Date(createSalaryStructureDto.effective_from),
      effective_to: createSalaryStructureDto.effective_to
        ? new Date(createSalaryStructureDto.effective_to)
        : null,
    }

    await this.salaryStructureRepository.save(salaryStructure)

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

  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,
      employee_id,
      department_id,
      status,
    } = query

    const skip = (page - 1) * limit
    const take = parseInt(limit.toString())

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

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

    if (employee_id) {
      whereConditions.employee_id = employee_id
    }

    if (department_id) {
      whereConditions.department_id = department_id
    }

    if (status !== undefined && status !== null) {
      whereConditions.status = status
    }

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

    const salaryStructures: any =
      await this.salaryStructureRepository.getByParams({
        where: whereConditions,
        search: !isEmpty(searchConditions) ? searchConditions : undefined,
        relations: [
          "company:id,name",
          "employee:id,name,email",
          "department:id,name",
        ],
        orderBy: { created_at: "DESC" },
        take,
        skip,
      })

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

  async findOne(id: number) {
    const salaryStructure: any =
      await this.salaryStructureRepository.getByParams({
        where: { id },
        whereNull: ["deleted_at"],
        relations: [
          "company:id,name",
          "employee:id,name,email",
          "department:id,name",
        ],
        findOne: true,
      })

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

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

  async update(
    id: number,
    updateSalaryStructureDto: UpdateSalaryStructureDto,
    token: string,
  ) {
    const decoded = verifyJwtToken(token)

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

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

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

    // Update the salary structure
    Object.assign(salaryStructure, updateSalaryStructureDto)

    if (updateSalaryStructureDto.effective_from) {
      salaryStructure.effective_from = new Date(
        updateSalaryStructureDto.effective_from,
      )
    }

    if (updateSalaryStructureDto.effective_to) {
      salaryStructure.effective_to = new Date(
        updateSalaryStructureDto.effective_to,
      )
    }

    salaryStructure.updated_by = decoded.user_id
    await this.salaryStructureRepository.save(salaryStructure)

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

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

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

    const salaryStructure: any =
      await this.salaryStructureRepository.getByParams({
        where: { id, company_id: decoded.company_id },
        whereNull: ["deleted_at"],
        findOne: true,
      })

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

    await this.salaryStructureRepository.remove({ id: salaryStructure.id })

    await this.salaryStructureRepository.save({
      id: salaryStructure.id,
      deleted_by: decoded.user_id,
    })

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

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

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

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

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

    salaryStructure.status = salaryStructure.status === 1 ? 0 : 1
    salaryStructure.updated_by = decoded.user_id

    await this.salaryStructureRepository.save(salaryStructure)

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

  async getActiveSalaryStructure(employeeId: number, companyId: number) {
    const salaryStructure = await this.salaryStructureRepository.getByParams({
      where: {
        employee_id: employeeId,
        company_id: companyId,
        status: 1,
      },
      whereNull: ["effective_to", "deleted_at"],
      relations: ["employee:id,name,email", "department:id,name"],
      findOne: true,
    })

    return salaryStructure
  }
}
