import { Injectable } from "@nestjs/common"
import { RoleRepository } from "./repositories/role.repository"
import { Role } from "./entities/role.entity"
import { verifyJwtToken } from "src/utils/jwt"
import { failureResponse, successResponse } from "src/common/response/response"
import { code } from "src/common/response/response.code"
import {
  errorMessage,
  isEmpty,
  successMessage,
  validationMessage,
} from "src/utils/helpers"
import { messageKey } from "src/constants/message-keys"
import { CreateRoleDto } from "./dto/create-role.dto"
import { UpdateRoleDto } from "./dto/update-role.dto"
import { RolePermissionsService } from "../role-permissions/role-permissions.service"
import { Repository } from "typeorm"
import { Employee } from "../employees/entities/employee.entity"
import { InjectRepository } from "@nestjs/typeorm"

@Injectable()
export class RoleService {
  constructor(
    private readonly roleRepository: RoleRepository,
    private readonly rolePermissionsService: RolePermissionsService,
    @InjectRepository(Employee)
    private readonly employeeEntityRepository: Repository<Employee>,
  ) {}

  async checkRoleExist(name: string, companyId?: number) {
    const whereCondition: any = { name: name }

    if (companyId !== undefined) {
      whereCondition.company_id = companyId
    }

    const role: any = await this.roleRepository.getByParams({
      where: whereCondition,
      whereNull: ["deleted_at"],
      findOne: true,
    })

    return !isEmpty(role)
  }

  async findRoleByNameAndCompany(name: string, companyId?: number) {
    const whereCondition: any = { name: name }

    if (companyId !== undefined) {
      whereCondition.company_id = companyId
    }

    const role: any = await this.roleRepository.getByParams({
      where: whereCondition,
      whereNull: ["deleted_at"],
      findOne: true,
    })

    return isEmpty(role) ? null : role
  }

  async create(createRoleDto: CreateRoleDto, token?: string) {
    const decoded = verifyJwtToken(token)

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

    // Check if role with same name exists for the company
    const existingRole = await this.roleRepository.getByParams({
      where: {
        company_id: decoded.company_id,
        name: createRoleDto.name,
      },
      whereNull: ["deleted_at"],
      findOne: true,
    })

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

    let role: any = new Role()
    role = {
      ...createRoleDto,
      status: createRoleDto.status || 1,
      company_id: decoded.company_id,
      created_by: decoded.user_id,
    }

    await this.roleRepository.save(role)

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

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

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

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

    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 roles: any = await this.roleRepository.getByParams({
      where: whereConditions,
      search: !isEmpty(searchConditions) ? searchConditions : undefined,
      relations: ["users"],
      whereNull: ["deleted_at"],
      orderBy: { created_at: "DESC" },
      take,
      skip,
    })

    // Fetch role usage count in employees table
    const roleCounts = await this.employeeEntityRepository
      .createQueryBuilder("emp")
      .select("emp.role_id", "role_id")
      .addSelect("COUNT(emp.id)", "count")
      .where("emp.company_id = :company_id", { company_id: decoded.company_id })
      .andWhere("emp.deleted_at IS NULL")
      .groupBy("emp.role_id")
      .getRawMany()

    const countMap = roleCounts.reduce((acc, cur) => {
      acc[cur.role_id] = Number(cur.count)
      return acc
    }, {})

    roles.data = roles.data.map((role) => {
      let count = countMap[role.id] || 0

      if (role.name?.toLowerCase() === "super admin") {
        count = count + 1
      }

      return {
        ...role,
        role_count: count,
      }
    })

    if (remove_admin === "true") {
      roles.data = roles.data.filter(
        (r) => !r.users.some((u) => u.slug === "super_admin"),
      )
    }

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

  async update(id: number, updateRoleDto: UpdateRoleDto, token: string) {
    const decoded = verifyJwtToken(token)

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

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

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

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

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

    // Update the role
    Object.assign(role, updateRoleDto)
    role.updated_by = decoded.user_id
    await this.roleRepository.save(role)

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

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

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

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

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

    await this.roleRepository.remove({ id: role.id })

    await this.roleRepository.save({
      id: role.id,
      deleted_by: decoded.user_id,
    })

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

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

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

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

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

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

    await this.roleRepository.save(role)

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.status_change, { ":data": "Role" }),
    )
  }
}
