import { Injectable } from "@nestjs/common"
import { RolePermissionRepository } from "./repositories/role-permission.repository"
import { RolePermission } from "./entities/role-permission.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 { CreateRolePermissionDto } from "./dto/create-role-permission.dto"
import { AssignPermissionsDto } from "./dto/assign-permissions.dto"
import { FindAllRolePermissionsDto } from "./dto/find-all-role-permissions.dto"

@Injectable()
export class RolePermissionsService {
  constructor(
    private readonly rolePermissionRepository: RolePermissionRepository,
  ) {}

  async create(
    createRolePermissionDto: CreateRolePermissionDto,
    token: string,
  ) {
    const decoded = verifyJwtToken(token)

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

    // Check if the role permission already exists
    const existingRolePermission =
      await this.rolePermissionRepository.getByParams({
        where: {
          role_id: decoded.role_id,
          permission_id: createRolePermissionDto.permission_id,
          company_id: decoded.company_id,
        },
        whereNull: ["deleted_at"],
        findOne: true,
      })

    if (existingRolePermission) {
      return failureResponse(
        code.VALIDATION,
        validationMessage(messageKey.already_exist, {
          ":data": "Role Permission",
          ":field": "combination",
        }),
      )
    }

    let rolePermission: any = new RolePermission()
    rolePermission = {
      ...createRolePermissionDto,
      role_id: decoded.role_id,
      company_id: decoded.company_id,
      status: createRolePermissionDto.status || 1,
      created_by: decoded.user_id,
    }

    await this.rolePermissionRepository.save(rolePermission)

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

  async assignPermissionsToRole(
    assignPermissionsDto: AssignPermissionsDto,
    token: string,
  ) {
    const decoded = verifyJwtToken(token)

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

    const { permission_id, role_id } = assignPermissionsDto

    // Check if permission already exists for this role
    const existingPermission = await this.rolePermissionRepository.getByParams({
      where: {
        role_id,
        permission_id: permission_id,
        company_id: decoded.company_id,
      },
      whereNull: ["deleted_at"],
      findOne: true,
    })

    if (existingPermission) {
      // If permission exists, remove it (toggle off)
      await this.rolePermissionRepository.remove({
        id: (existingPermission as any).id,
      })
      await this.rolePermissionRepository.save({
        id: (existingPermission as any).id,
        deleted_by: decoded.user_id,
      })

      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_update, { ":data": "Role Permission" }),
      )
    } else {
      // If permission doesn't exist, add it (toggle on)
      const rolePermission: any = {
        role_id,
        permission_id,
        company_id: decoded.company_id,
        status: 1,
        created_by: decoded.user_id,
      }

      await this.rolePermissionRepository.save(rolePermission)

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

  async getRolePermissions(token: string) {
    const decoded = verifyJwtToken(token)

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

    const where: any = {
      role_id: decoded.role_id,
      status: 1,
    }

    if (decoded.company_id) {
      where.company_id = decoded.company_id
    }

    const rolePermissions: any =
      await this.rolePermissionRepository.getByParams({
        where,
        whereNull: ["deleted_at"],
        relations: ["permission"],
        select: ["id", "permission_id"],
        orderBy: { id: "ASC" },
      })

    if (isEmpty(rolePermissions)) {
      return successResponse(
        code.SUCCESS,
        successMessage(messageKey.data_retrieve, {
          ":data": "Role Permissions",
        }),
        {} as any,
      )
    }

    const permissions = Array.isArray(rolePermissions)
      ? rolePermissions
      : [rolePermissions]

    // Group permissions by group_key
    const groupedPermissions: Record<string, any[]> = {}

    permissions.forEach((rp: any) => {
      if (rp.permission && rp.permission.status === 1) {
        const groupKey = rp.permission.group_key
        if (!groupedPermissions[groupKey]) {
          groupedPermissions[groupKey] = []
        }
        groupedPermissions[groupKey].push({
          id: rp.permission.id,
          permission_key: rp.permission.permission_key,
          label: rp.permission.label,
          description: rp.permission.description,
        })
      }
    })

    return successResponse(
      code.SUCCESS,
      successMessage(messageKey.data_retrieve, { ":data": "Role Permissions" }),
      groupedPermissions as any,
    )
  }

  async getUserPermissions(token: string) {
    const decoded = verifyJwtToken(token)

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

    const { company_id } = decoded

    const rolePermissions: any =
      await this.rolePermissionRepository.getByParams({
        where: {
          company_id: company_id || undefined,
          status: 1,
        },
        whereNull: ["deleted_at"],
        relations: ["permission"],
      })

    if (isEmpty(rolePermissions)) {
      return []
    }

    const permissions = Array.isArray(rolePermissions)
      ? rolePermissions
      : [rolePermissions]

    return permissions
      .filter((rp: any) => rp.permission && rp.permission.status === 1)
      .map((rp: any) => rp.permission.permission_key)
  }

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

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

    const { page = 1, limit = 10, search, status } = query

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

    const whereConditions: any = {
      company_id: decoded.company_id,
    }

    const searchConditions: any = {}

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

    // Add search functionality (can search by role name or permission key)
    if (search) {
      searchConditions.role = { name: search }
    }

    const rolePermissions: any =
      await this.rolePermissionRepository.getByParams({
        where: whereConditions,
        search: !isEmpty(searchConditions) ? searchConditions : undefined,
        whereNull: ["deleted_at"],
        relations: ["role", "permission"],
        orderBy: { created_at: "DESC" },
        take,
        skip,
      })

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

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

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

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

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

    await this.rolePermissionRepository.remove({ id: rolePermission.id })

    await this.rolePermissionRepository.save({
      id: rolePermission.id,
      deleted_by: decoded.user_id,
    })

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

  async findOne(id: number) {
    const rolePermission: any = await this.rolePermissionRepository.getByParams(
      {
        where: { id },
        whereNull: ["deleted_at"],
        relations: ["role", "permission"],
        findOne: true,
      },
    )

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

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

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

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

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

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

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

    await this.rolePermissionRepository.save(rolePermission)

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

  async checkRolePermissionExist(
    roleId: number,
    permissionId: number,
    companyId: number,
  ) {
    const rolePermission = await this.rolePermissionRepository.getByParams({
      where: {
        role_id: roleId,
        permission_id: permissionId,
        company_id: companyId,
      },
      whereNull: ["deleted_at"],
      findOne: true,
    })

    return !isEmpty(rolePermission)
  }
}
