import { Injectable } from "@nestjs/common"
import { CreateDepartmentDto } from "./dto/create-department.dto"
import { UpdateDepartmentDto } from "./dto/update-department.dto"
import { DepartmentRepository } from "./repositories/department.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 { Department } from "./entities/department.entity"
import { verifyJwtToken } from "src/utils/jwt"
import { CreateSubDepartmentDto } from "./dto/create-sub-department.dto"

@Injectable()
export class DepartmentsService {
  constructor(private readonly departmentRepository: DepartmentRepository) {}

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

    // Check if department with same name exists for the company
    const existingDepartment = await this.departmentRepository.getByParams({
      where: {
        company_id: decoded.company_id,
        name: createDepartmentDto.name,
      },
      findOne: true,
    })

    if (existingDepartment) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, {
          ":data": "Department",
          ":field": "name",
        }),
      )
    }

    let department: any = new Department()
    department = {
      ...createDepartmentDto,
      status: createDepartmentDto.status || 1,
      company_id: decoded.company_id,
      created_by: decoded.user_id,
    }

    await this.departmentRepository.save(department)

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

  async createSubDepartment(
    createSubDepartmentDto: CreateSubDepartmentDto,
    token: string,
  ) {
    try {
      const decoded = verifyJwtToken(token)

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

      const parentDepartment: any = await this.departmentRepository.getByParams(
        {
          where: { id: createSubDepartmentDto.parent_id },
          findOne: true,
        },
      )

      if (!parentDepartment) {
        return failureResponse(
          code.ERROR,
          errorMessage(messageKey.not_found, {
            ":data": "Parent Department",
          }),
        )
      }

      if (parentDepartment?.company_id !== decoded.company_id) {
        return failureResponse(
          code.ERROR,
          errorMessage(messageKey.not_found, {
            ":data": "Company",
          }),
        )
      }

      if (parentDepartment?.parent_id) {
        return failureResponse(
          code.ERROR,
          "Cannot create sub-department under another sub-department",
        )
      }

      const name = createSubDepartmentDto.name.trim().toLowerCase()

      // Check if department with same name exists for the company
      const existingSubDepartment = await this.departmentRepository.getByParams(
        {
          where: {
            company_id: decoded.company_id,
            parent_id: createSubDepartmentDto.parent_id,
            name: name,
          },
          findOne: true,
        },
      )

      if (existingSubDepartment) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.already_exist, {
            ":data": "Sub Department",
            ":field": "name",
          }),
        )
      }

      let department: any = new Department()

      department = {
        ...createSubDepartmentDto,
        status: 1,
        company_id: decoded.company_id,
        created_by: decoded.user_id,
      }

      await this.departmentRepository.save(department)

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_add, { ":data": "Sub Department" }),
      )
    } catch (error) {
      return failureResponse(code.ERROR, errorMessage(messageKey.exception))
    }
  }

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

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

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

      const orderDirection = isEmpty(order) ? "DESC" : order.toUpperCase()

      // Set up dynamic ordering
      let orderBy: any = { created_at: orderDirection }

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

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

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

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

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

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

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, { ":data": "Departments" }),
        departments,
      )
    } catch (error) {
      console.log(error)
    }
  }

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

    if (isEmpty(department)) {
      return failureResponse(
        code.VALIDATION,
        errorMessage(messageKey.data_not_found, { ":data": "Department" }),
      )
    }

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

  async update(
    id: number,
    updateDepartmentDto: UpdateDepartmentDto,
    token: string,
  ) {
    const decoded = verifyJwtToken(token)

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

    const department: any = await this.departmentRepository.getByParams({
      where: { id },
      findOne: true,
    })

    if (isEmpty(department)) {
      return failureResponse(
        code.VALIDATION,
        errorMessage(messageKey.data_not_found, { ":data": "Department" }),
      )
    }

    // Check if department with same name exists for the company (excluding current record)
    if (updateDepartmentDto.name) {
      const existingDepartment = await this.departmentRepository.getByParams({
        where: {
          company_id: decoded.company_id,
          name: updateDepartmentDto.name,
        },
        whereNull: ["deleted_at"],
        whereNotIn: { id: [id] },
        findOne: true,
      })

      if (existingDepartment) {
        return failureResponse(
          code.VALIDATION,
          validationMessage(messageKey.already_exist, {
            ":data": "Department",
            ":field": "name",
          }),
        )
      }
    }

    // Update the department
    Object.assign(department, updateDepartmentDto)
    department.updated_by = decoded.user_id
    await this.departmentRepository.save(department)

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

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

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

    const department: any = await this.departmentRepository.getByParams({
      where: { id },
      whereNull: ["deleted_at"],
      findOne: true,
    })

    if (isEmpty(department)) {
      return failureResponse(
        code.VALIDATION,
        errorMessage(messageKey.data_not_found, { ":data": "Department" }),
      )
    }

    await this.departmentRepository.remove({ id: department.id })

    await this.departmentRepository.save({
      id: department.id,
      deleted_by: decoded.user_id,
    })

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

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

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

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

    if (isEmpty(department)) {
      return failureResponse(
        code.VALIDATION,
        errorMessage(messageKey.data_not_found, { ":data": "Department" }),
      )
    }

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

    await this.departmentRepository.save(department)

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

  async checkDepartmentExist(name: string, companyId: number) {
    const department = await this.departmentRepository.getByParams({
      where: {
        name: name,
        company_id: companyId,
      },
      findOne: true,
    })

    return !isEmpty(department)
  }
}
