import { Injectable, NotFoundException, ConflictException, BadRequestException, Logger } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { FindManyOptions, Repository } from 'typeorm';
import { RoleEntity } from './entities/role.entity';
import { CreateRoleDto } from './dto/create-role.dto';
import { RoleResponseDto } from './dto/role-response.dto';
import { UpdateRoleDto } from './dto/update-role.dto';
import { PaginatedResponseDto } from 'common/utils/paginated-response.dto';
import { mapUserToResponseDto, UserResponseDto } from 'modules/user/dto/user-response.dto';
import { UserPermissionOverrideEntity } from './entities/user-permission-override.entity';
import { RolePermissionStateEntity } from './entities/role-permission-state.entity';
import { PermissionWithStateDto, SubPermissionWithStateDto } from '../permissions/dto/permission-state.dto';
import { PermissionEntity } from '../permissions/entities/permission.entity';
import { SubPermissionEntity } from '../permissions/entities/sub-permission.entity';
import { PermissionState } from 'common/enum/permission-state.enum';
import { PermissionStateDto } from './dto/permission-state.dto';
import { UserPermissionOverrideDto } from './dto/user-permission-override.dto';
import { EffectivePermissionDto } from './dto/effective-permission.dto';
import { UserEntity } from 'modules/user/entity/users.entity';
import { BaseRoleService } from './abstract/role.abstract';
@Injectable()
export class RoleService extends BaseRoleService {
  private readonly logger = new Logger(RoleService.name);

  constructor(
    @InjectRepository(RoleEntity)
    private readonly roleRepository: Repository<RoleEntity>,
    @InjectRepository(UserEntity)
    private readonly userRepository: Repository<UserEntity>,
    @InjectRepository(PermissionEntity)
    private readonly permissionRepository: Repository<PermissionEntity>,
    @InjectRepository(SubPermissionEntity)
    private readonly subPermissionRepository: Repository<SubPermissionEntity>,
    @InjectRepository(UserPermissionOverrideEntity)
    private userOverrideRepository: Repository<UserPermissionOverrideEntity>,
    @InjectRepository(RolePermissionStateEntity)
    private rolePermissionStateRepository: Repository<RolePermissionStateEntity>,
  ) {
    super();
  }

  async createRole(createRoleDto: CreateRoleDto): Promise<RoleResponseDto> {
    try {
      const role = new RoleEntity();
      role.name = createRoleDto.name;
      role.roleType = createRoleDto.roleType;
      role.isCustom = createRoleDto.isCustom ?? false;
      role.priority = 0;

      if (createRoleDto.clientId) {
        role.client = { id: createRoleDto.clientId } as any;
      }

      const savedRole = await this.roleRepository.save(role);

      // Set permission states if provided
      if (createRoleDto.permissions?.length || createRoleDto.subPermissions?.length) {
        await this.bulkSetRolePermissionStates(savedRole.id, createRoleDto.permissions || [], createRoleDto.subPermissions || []);
      }

      const createdRole = await this.roleRepository.findOne({
        where: { id: savedRole.id },
        // relations: [],
      });

      if (!createdRole) {
        throw new Error('Failed to create role');
      }

      return this.mapRoleToResponseDto(createdRole);
    } catch (error) {
      this.logger.error(`Failed to create role: ${error.message}`, error.stack);
      throw new BadRequestException(`Failed to create role: ${error.message}`);
    }
  }

  async updateRole(roleId: string, updateRoleDto: UpdateRoleDto): Promise<RoleResponseDto> {
    try {
      const role = await this.roleRepository.findOne({ where: { id: roleId } });
      if (!role) {
        throw new Error('Role not found');
      }

      // Update basic properties
      if (updateRoleDto.name) role.name = updateRoleDto.name;
      if (updateRoleDto.priority !== undefined) role.priority = updateRoleDto.priority;

      await this.roleRepository.save(role);

      // Update permission states if provided
      if (updateRoleDto.permissions?.length || updateRoleDto.subPermissions?.length) {
        await this.bulkSetRolePermissionStates(roleId, updateRoleDto.permissions || [], updateRoleDto.subPermissions || []);
      }

      return this.findRoleById(roleId);
    } catch (error) {
      this.logger.error(`Failed to update role ${roleId}: ${error.message}`, error.stack);
      throw new BadRequestException(`Failed to update role: ${error.message}`);
    }
  }

  async deleteRole(roleId: string): Promise<void> {
    try {
      const role = await this.roleRepository.findOne({
        where: { id: roleId },
        relations: ['users'],
      });

      if (!role) {
        throw new Error('Role not found');
      }

      if (role.users?.length > 0) {
        throw new Error('Cannot delete role with assigned users');
      }

      // Delete related permission states
      await this.rolePermissionStateRepository.delete({ roleId });
      await this.roleRepository.delete({ id: roleId });
    } catch (error) {
      this.logger.error(`Failed to delete role ${roleId}: ${error.message}`, error.stack);
      throw new BadRequestException(`Failed to delete role: ${error.message}`);
    }
  }

  async findAllRoles(page: number, limit: number, clientId?: string): Promise<PaginatedResponseDto<RoleResponseDto>> {
    const options: FindManyOptions<RoleEntity> = {
      relations: ['permissionStates'],
      skip: (page - 1) * limit,
      take: limit,
      order: { priority: 'DESC', createdAt: 'DESC' },
    };

    if (clientId) {
      options.where = { client: { id: clientId } };
    }

    const [roles, total] = await this.roleRepository.findAndCount(options);

    console.log(`${JSON.stringify(roles.map(role => role))} - Total: ${total}`);

    const data = await Promise.all(roles.map(role => this.mapRoleToResponseDto(role)));

    console.log(`Mapped roles: ${JSON.stringify(data)}`);

    return {
      items: data,
      currentPage: page,
      totalItems: total,
      totalPages: Math.ceil(total / limit),
      itemsPerPage: limit,
    };
  }

  async findRoleById(roleId: string): Promise<RoleResponseDto> {
    const role = await this.roleRepository.findOne({
      where: { id: roleId },
      relations: ['permissionStates', 'permissionStates.permission', 'permissionStates.subPermission', 'client', 'users'],
    });

    if (!role) {
      throw new Error('Role not found');
    }

    return this.mapRoleToResponseDto(role);
  }

  async findRoleByName(roleName: string, clientId?: string): Promise<RoleResponseDto> {
    try {
      const whereCondition: any = { name: roleName };
      if (clientId) {
        whereCondition.client = { id: clientId };
      }

      const role = await this.roleRepository.findOne({
        where: whereCondition,
        relations: ['permissionStates', 'permissionStates.permission', 'permissionStates.subPermission', 'client', 'users'],
      });

      if (!role) {
        throw new Error('Role not found');
      }

      return this.mapRoleToResponseDto(role);
    } catch (error) {
      this.logger.error(`Failed to find role by name ${roleName}: ${error.message}`, error.stack);
      throw new NotFoundException(`Role not found: ${error.message}`);
    }
  }

  async removeUserFromRole(userId: number, roleId: string): Promise<void> {
    try {
      const user = await this.userRepository.findOne({
        where: { id: userId },
        relations: ['roles'],
      });

      this.logger.debug(`Removing user with ID: ${user.email} from role with ID: ${roleId}`);
      if (!user) {
        throw new Error('User not found');
      }

      user.roles = user.roles.filter(role => role.id !== roleId);
      this.logger.debug(`User roles after removal: ${user.roles.map(role => role.name).join(', ')}`);
      await this.userRepository.save(user);
    } catch (error) {
      this.logger.error(`Failed to remove user ${userId} from role ${roleId}: ${error.message}`, error.stack);
      throw new BadRequestException(`Failed to remove user from role: ${error.message}`);
    }
  }

  async revokeUserAllRoles(userId: number): Promise<void> {
    try {
      const user = await this.userRepository.findOne({
        where: { id: userId },
        relations: ['roles'],
      });

      if (!user) {
        throw new Error('User not found');
      }

      user.roles = [];
      await this.revokeUserAllRoles(userId);
    } catch (error) {
      this.logger.error(`Failed to revoke all roles for user ${userId}: ${error.message}`, error.stack);
      throw new BadRequestException(`Failed to revoke user roles: ${error.message}`);
    }
  }

  async removeUserRolesFromClient(userId: number, clientId: string): Promise<void> {
    try {
      const user = await this.userRepository.findOne({
        where: { id: userId },
        relations: ['roles'],
      });

      if (!user) {
        throw new Error('User not found');
      }

      user.roles = user.roles.filter(role => role.client.id == clientId);
      await this.removeUserRolesFromClient(userId, clientId);
    } catch (error) {
      this.logger.error(`Failed to remove user ${userId} roles from client ${clientId}: ${error.message}`, error.stack);
      throw new BadRequestException(`Failed to remove user roles from client: ${error.message}`);
    }
  }

  async setRolePermissionState(roleId: string, permissionId: string, state: any): Promise<void> {
    try {
      if (!Object.values(PermissionState).includes(state)) {
        throw new BadRequestException('Invalid permission state');
      }

      await this.upsertRolePermissionState(roleId, permissionId, null, state);
    } catch (error) {
      this.logger.error(`Failed to set role permission state for role ${roleId} and permission ${permissionId}: ${error.message}`, error.stack);
      throw new BadRequestException(`Failed to set role permission state: ${error.message}`);
    }
  }

  async setRoleSubPermissionState(roleId: string, subPermissionId: string, state: any): Promise<void> {
    try {
      if (!Object.values(PermissionState).includes(state)) {
        throw new BadRequestException('Invalid permission state');
      }
      await this.upsertRolePermissionState(roleId, null, subPermissionId, state);
    } catch (error) {
      this.logger.error(
        `Failed to set role sub-permission state for role ${roleId} and sub-permission ${subPermissionId}: ${error.message}`,
        error.stack,
      );
      throw new BadRequestException(`Failed to set role sub-permission state: ${error.message}`);
    }
  }

  async bulkSetRolePermissionStates(
    roleId: string,
    permissions: PermissionStateDto[],
    subPermissions: PermissionStateDto[],
  ): Promise<RoleResponseDto> {
    try {
      // Remove existing states if replacing all
      await this.rolePermissionStateRepository.delete({ roleId });

      // Add new permission states
      for (const perm of permissions) {
        if (perm.state !== PermissionState.INHERIT) {
          await this.upsertRolePermissionState(roleId, perm.id, null, perm.state);
        }
      }

      for (const subPerm of subPermissions) {
        if (subPerm.state !== PermissionState.INHERIT) {
          await this.upsertRolePermissionState(roleId, null, subPerm.id, subPerm.state);
        }
      }

      return this.findRoleById(roleId);
    } catch (error) {
      this.logger.error(`Failed to bulk set role permission states for role ${roleId}: ${error.message}`, error.stack);
      throw new BadRequestException(`Failed to bulk set role permission states: ${error.message}`);
    }
  }

  async setUserPermissionOverride(override: UserPermissionOverrideDto): Promise<void> {
    try {
      const existing = await this.userOverrideRepository.findOne({
        where: {
          userId: override.userId,
          permissionId: override.permissionId || null,
          subPermissionId: override.subPermissionId || null,
          clientId: override.clientId || null,
        },
      });

      if (existing) {
        if (override.state === PermissionState.INHERIT) {
          // Remove override to inherit from roles
          await this.userOverrideRepository.delete({ id: existing.id });
        } else {
          existing.permissionState = override.state;
          await this.userOverrideRepository.save(existing);
        }
      } else if (override.state !== PermissionState.INHERIT) {
        const newOverride = new UserPermissionOverrideEntity();
        Object.assign(newOverride, override);
        newOverride.permissionState = override.state;
        await this.userOverrideRepository.save(newOverride);
      }
    } catch (error) {
      this.logger.error(`Failed to set user permission override for user ${override.userId}: ${error.message}`, error.stack);
      throw new BadRequestException(`Failed to set user permission override: ${error.message}`);
    }
  }

  async removeUserPermissionOverride(userId: number, permissionId?: string, subPermissionId?: string, clientId?: string): Promise<void> {
    try {
      const whereCondition: any = { userId };
      if (permissionId) whereCondition.permissionId = permissionId;
      if (subPermissionId) whereCondition.subPermissionId = subPermissionId;
      if (clientId) whereCondition.clientId = clientId;

      await this.userOverrideRepository.delete(whereCondition);
    } catch (error) {
      this.logger.error(`Failed to remove user permission override for user ${userId}: ${error.message}`, error.stack);
      throw new BadRequestException(`Failed to remove user permission override: ${error.message}`);
    }
  }

  async getUserPermissionOverrides(userId: number, clientId?: string): Promise<UserPermissionOverrideEntity[]> {
    try {
      const whereCondition: any = { userId };
      if (clientId) whereCondition.clientId = clientId;

      return this.userOverrideRepository.find({
        where: whereCondition,
        relations: ['permission', 'subPermission'],
      });
    } catch (error) {
      this.logger.error(`Failed to get user permission overrides for user ${userId}: ${error.message}`, error.stack);
      throw new BadRequestException(`Failed to get user permission overrides: ${error.message}`);
    }
  }

  async checkUserPermission(userId: number, permissionName: string, clientId?: string): Promise<boolean> {
    return this.checkPermission(userId, permissionName, null, clientId);
  }

  async checkUserSubPermission(userId: number, subPermissionName: string, clientId?: string): Promise<boolean> {
    return this.checkPermission(userId, null, subPermissionName, clientId);
  }

  async getUserEffectivePermissions(userId: number, clientId?: string): Promise<EffectivePermissionDto[]> {
    try {
      const effectivePermissions: EffectivePermissionDto[] = [];

      // Get all permissions and subPermissions
      const permissions = await this.permissionRepository.find({ where: { isActive: true } });
      const subPermissions = await this.subPermissionRepository.find({ where: { isActive: true } });

      // Check effective state for each permission
      for (const permission of permissions) {
        const effectiveState = await this.getEffectivePermissionState(userId, permission.name, null, clientId);
        effectivePermissions.push({
          id: permission.id,
          name: permission.name,
          displayName: permission.displayName,
          type: 'permission',
          state: effectiveState.state,
          source: effectiveState.source,
          sourceDetails: effectiveState.sourceDetails,
        });
      }

      for (const subPermission of subPermissions) {
        const effectiveState = await this.getEffectivePermissionState(userId, null, subPermission.name, clientId);
        effectivePermissions.push({
          id: subPermission.id,
          name: subPermission.name,
          displayName: subPermission.displayName,
          type: 'subPermission',
          state: effectiveState.state,
          source: effectiveState.source,
          sourceDetails: effectiveState.sourceDetails,
        });
      }

      return effectivePermissions;
    } catch (error) {
      this.logger.error(`Failed to get effective permissions for user ${userId}: ${error.message}`, error.stack);
      throw new BadRequestException(`Failed to get effective permissions: ${error.message}`);
    }
  }

  // Legacy methods implementation
  async getPermissionsByRoleId(roleId: string): Promise<PermissionWithStateDto[]> {
    try {
      const role = await this.roleRepository.findOne({
        where: { id: roleId },
        relations: ['permissionStates', 'permissionStates.permission'],
      });

      if (!role) return [];

      const permissions = await this.permissionRepository.find({ where: { isActive: true } });

      return permissions.map(permission => {
        const state = role.permissionStates?.find(ps => ps.permissionId === permission.id);
        return {
          id: permission.id,
          name: permission.name,
          displayName: permission.displayName,
          description: permission.description,
          state: state?.permissionState || PermissionState.INHERIT,
          isActive: permission.isActive,
        };
      });
    } catch (error) {
      this.logger.error(`Failed to get permissions for role ${roleId}: ${error.message}`, error.stack);
      throw new BadRequestException(`Failed to get permissions: ${error.message}`);
    }
  }

  async getSubPermissionsByRoleId(roleId: string): Promise<SubPermissionWithStateDto[]> {
    try {
      const role = await this.roleRepository.findOne({
        where: { id: roleId },
        relations: ['permissionStates', 'permissionStates.subPermission'],
      });

      if (!role) return [];

      const subPermissions = await this.subPermissionRepository.find({ where: { isActive: true } });

      return subPermissions.map(subPermission => {
        const state = role.permissionStates?.find(ps => ps.subPermissionId === subPermission.id);
        return {
          id: subPermission.id,
          name: subPermission.name,
          displayName: subPermission.displayName,
          description: subPermission.description,
          state: state?.permissionState || PermissionState.INHERIT,
          isActive: subPermission.isActive,
          permissionId: subPermission.permissionId,
        };
      });
    } catch (error) {
      this.logger.error(`Failed to get sub-permissions for role ${roleId}: ${error.message}`, error.stack);
      throw new BadRequestException(`Failed to get sub-permissions: ${error.message}`);
    }
  }

  async getPermissionsByUserId(userId: number, clientId?: string): Promise<EffectivePermissionDto[]> {
    try {
      const effective = await this.getUserEffectivePermissions(userId, clientId);
      return effective.filter(p => p.type === 'permission');
    } catch (error) {
      this.logger.error(`Failed to get permissions for user ${userId}: ${error.message}`, error.stack);
      throw new BadRequestException(`Failed to get user permissions: ${error.message}`);
    }
  }

  async getSubPermissionsByUserId(userId: number, clientId?: string): Promise<EffectivePermissionDto[]> {
    try {
      const effective = await this.getUserEffectivePermissions(userId, clientId);
      return effective.filter(p => p.type === 'subPermission');
    } catch (error) {
      this.logger.error(`Failed to get sub-permissions for user ${userId}: ${error.message}`, error.stack);
      throw new BadRequestException(`Failed to get user sub-permissions: ${error.message}`);
    }
  }

  async getPermissionsByRoleName(roleName: string, clientId?: string): Promise<PermissionWithStateDto[]> {
    try {
      const role = await this.findRoleByName(roleName, clientId);
      return this.getPermissionsByRoleId(role.id);
    } catch (error) {
      this.logger.error(`Failed to get permissions for role ${roleName}: ${error.message}`, error.stack);
      throw new BadRequestException(`Failed to get permissions by role name: ${error.message}`);
    }
  }

  async getRolesByUserId(userId: number): Promise<RoleResponseDto[]> {
    try {
      const user = await this.userRepository.findOne({
        where: { id: userId },
        relations: ['roles', 'roles.permissionStates', 'roles.permissionStates.permission', 'roles.permissionStates.subPermission'],
      });

      if (!user) {
        throw new Error('User not found');
      }
      const roles = user.roles;
      return Promise.all(roles.map(role => this.mapRoleToResponseDto(role)));
    } catch (error) {
      this.logger.error(`Failed to get roles for user ${userId}: ${error.message}`, error.stack);
      throw new BadRequestException(`Failed to get user roles: ${error.message}`);
    }
  }

  async getRolesByClientId(clientId: string): Promise<RoleResponseDto[]> {
    try {
      const roles = await this.roleRepository.find({
        where: { client: { id: clientId } },
        relations: ['permissionStates', 'permissionStates.permission', 'permissionStates.subPermission'],
        order: { priority: 'DESC' },
      });

      return Promise.all(roles.map(role => this.mapRoleToResponseDto(role)));
    } catch (error) {
      this.logger.error(`Failed to get roles for client ${clientId}: ${error.message}`, error.stack);
      throw new BadRequestException(`Failed to get roles by client ID: ${error.message}`);
    }
  }

  async getUsersByRoleId(roleId: string): Promise<UserResponseDto[]> {
    const role = await this.roleRepository.findOne({
      where: { id: roleId },
      relations: ['users', 'users.roles'],
    });

    if (!role) {
      throw new Error('Role not found');
    }

    return Promise.all(role.users.map(user => mapUserToResponseDto(user)));
  }

  async getRolesByUserEmail(email: string, clientId?: string): Promise<RoleResponseDto[]> {
    const user = await this.userRepository.findOne({
      where: { email },
      relations: ['roles', 'roles.permissionStates', 'roles.permissionStates.permission', 'roles.permissionStates.subPermission'],
    });

    if (!user) {
      throw new Error('User not found');
    }

    let roles = user.roles;
    if (clientId) {
      roles = roles.filter(role => role.client?.id === clientId);
    }

    return Promise.all(roles.map(role => this.mapRoleToResponseDto(role)));
  }

  async duplicateRole(roleId: string, newName: string): Promise<RoleResponseDto> {
    const originalRole = await this.roleRepository.findOne({
      where: { id: roleId },
      relations: ['permissionStates', 'client'],
    });

    if (!originalRole) {
      throw new Error('Role not found');
    }

    const newRole = new RoleEntity();
    newRole.name = newName;
    newRole.roleType = originalRole.roleType;
    newRole.isCustom = true; // Duplicated roles are custom
    newRole.priority = originalRole.priority;
    newRole.client = originalRole.client;

    const savedRole = await this.roleRepository.save(newRole);

    // Copy permission states
    for (const permState of originalRole.permissionStates) {
      const newPermState = new RolePermissionStateEntity();
      newPermState.roleId = savedRole.id;
      newPermState.permissionId = permState.permissionId;
      newPermState.subPermissionId = permState.subPermissionId;
      newPermState.permissionState = permState.permissionState;
      await this.rolePermissionStateRepository.save(newPermState);
    }

    return this.findRoleById(savedRole.id);
  }

  async getRoleHierarchy(clientId?: string): Promise<RoleResponseDto[]> {
    const whereCondition: any = {};
    if (clientId) {
      whereCondition.client = { id: clientId };
    }

    const roles = await this.roleRepository.find({
      where: whereCondition,
      relations: ['permissionStates', 'permissionStates.permission', 'permissionStates.subPermission'],
      order: { priority: 'DESC', name: 'ASC' },
    });

    return Promise.all(roles.map(role => this.mapRoleToResponseDto(role)));
  }

  async assignUserToRoleByEmail(email: string, roleId: string, clientId?: string): Promise<void> {
    const user = await this.userRepository.findOne({
      where: { email },
      relations: ['roles'],
    });

    if (!user) {
      throw new NotFoundException(`User with email ${email} not found`);
    }

    const role = await this.roleRepository.findOne({
      where: { id: roleId, ...(clientId ? { client: { id: clientId } } : {}) },
    });

    if (!role) {
      throw new NotFoundException(`Role with ID ${roleId} not found`);
    }

    // Check if user already has the role
    if (user.roles.some(r => r.id === roleId)) {
      throw new ConflictException(`User already has role ${role.name}`);
    }

    user.roles.push(role);
    await this.userRepository.save(user);
  }

  // Private helper methods
  // --------------------------------------------------------------

  private async getEffectivePermissionState(
    userId: number,
    permissionName: string | null,
    subPermissionName: string | null,
    clientId?: string,
  ): Promise<{ state: PermissionState; source: 'user_override' | 'role' | 'default'; sourceDetails: string }> {
    // Check user override first
    const userOverride = await this.getUserPermissionOverride(userId, permissionName, subPermissionName, clientId);
    if (userOverride) {
      if (userOverride.permissionState === PermissionState.ALLOW) {
        return { state: PermissionState.ALLOW, source: 'user_override', sourceDetails: 'User Override' };
      }
      if (userOverride.permissionState === PermissionState.DENY) {
        return { state: PermissionState.DENY, source: 'user_override', sourceDetails: 'User Override' };
      }
    }

    // Check role permissions
    const user = await this.userRepository.findOne({
      where: { id: userId },
      relations: ['roles', 'roles.permissionStates', 'roles.permissionStates.permission', 'roles.permissionStates.subPermission'],
    });

    if (user?.roles?.length) {
      const sortedRoles = user.roles.sort((a, b) => b.priority - a.priority);

      for (const role of sortedRoles) {
        for (const permissionState of role.permissionStates) {
          const matchesPermission = subPermissionName
            ? permissionState.subPermission?.name === subPermissionName
            : permissionState.permission?.name === permissionName;

          if (matchesPermission) {
            if (permissionState.permissionState === PermissionState.ALLOW) {
              return { state: PermissionState.ALLOW, source: 'role', sourceDetails: role.name };
            }
            if (permissionState.permissionState === PermissionState.DENY) {
              return { state: PermissionState.DENY, source: 'role', sourceDetails: role.name };
            }
          }
        }
      }
    }

    // Default state
    return { state: PermissionState.DENY, source: 'default', sourceDetails: 'Default Deny' };
  }

  private async getRolePermissions(userId: number, permissionName: string | null, subPermissionName: string | null): Promise<boolean | null> {
    const user = await this.userRepository.findOne({
      where: { id: userId },
      relations: ['roles', 'roles.permissionStates', 'roles.permissionStates.permission', 'roles.permissionStates.subPermission'],
    });

    if (!user?.roles?.length) return null;

    // Sort roles by priority (highest first)
    const sortedRoles = user.roles.sort((a, b) => b.priority - a.priority);

    for (const role of sortedRoles) {
      for (const permissionState of role.permissionStates) {
        const matchesPermission = subPermissionName
          ? permissionState.subPermission?.name === subPermissionName
          : permissionState.permission?.name === permissionName;

        if (matchesPermission) {
          if (permissionState.permissionState === PermissionState.ALLOW) return true;
          if (permissionState.permissionState === PermissionState.DENY) return false;
        }
      }
    }

    return null;
  }

  private async checkPermission(
    userId: number,
    permissionName: string | null,
    subPermissionName: string | null,
    clientId?: string,
  ): Promise<boolean> {
    // 1. Check user overrides first
    this.logger.debug(
      `Checking permission for user ${userId} - Permission: ${permissionName}, SubPermission: ${subPermissionName}, Client: ${clientId}`,
    );
    const userOverride = await this.getUserPermissionOverride(userId, permissionName, subPermissionName, clientId);
    if (userOverride) {
      if (userOverride.permissionState === PermissionState.ALLOW) {
        this.logger.debug(
          `User override found for user ${userId} - Permission: ${permissionName}, SubPermission: ${subPermissionName}, State: ALLOW`,
        );
        return true;
      }
      if (userOverride.permissionState === PermissionState.DENY) {
        this.logger.debug(`User override found for user ${userId} - Permission: ${permissionName}, SubPermission: ${subPermissionName}, State: DENY`);
        return false;
      }
    }

    // 2. Check role permissions
    this.logger.debug(`Checking role permissions for user ${userId} - Permission: ${permissionName}, SubPermission: ${subPermissionName}`);
    const rolePermission = await this.getRolePermissions(userId, permissionName, subPermissionName);
    if (rolePermission === true) {
      this.logger.debug(`Role permission ALLOW found for user ${userId} - Permission: ${permissionName}, SubPermission: ${subPermissionName}`);
      return true;
    }

    if (rolePermission !== null) {
      return rolePermission;
    }

    // 3. Default deny
    return false;
  }

  private async getUserPermissionOverride(
    userId: number,
    permissionName: string | null,
    subPermissionName: string | null,
    clientId?: string,
  ): Promise<UserPermissionOverrideEntity | null> {
    const queryBuilder = this.userOverrideRepository
      .createQueryBuilder('override')
      .leftJoinAndSelect('override.permission', 'permission')
      .leftJoinAndSelect('override.subPermission', 'subPermission')
      .where('override.userId = :userId', { userId });

    if (subPermissionName) {
      queryBuilder.andWhere('subPermission.name = :subPermissionName', { subPermissionName });
    } else if (permissionName) {
      queryBuilder.andWhere('permission.name = :permissionName', { permissionName });
    }

    if (clientId) {
      queryBuilder.andWhere('override.clientId = :clientId', { clientId });
    }

    return await queryBuilder.getOne();
  }

  private async upsertRolePermissionState(
    roleId: string,
    permissionId: string | null,
    subPermissionId: string | null,
    state: PermissionState,
  ): Promise<void> {
    const existing = await this.rolePermissionStateRepository.findOne({
      where: { roleId, permissionId, subPermissionId },
    });

    if (existing) {
      if (state === PermissionState.INHERIT) {
        await this.rolePermissionStateRepository.delete({ id: existing.id });
      } else {
        existing.permissionState = state;
        await this.rolePermissionStateRepository.save(existing);
      }
    } else if (state !== PermissionState.INHERIT) {
      const newState = new RolePermissionStateEntity();
      newState.roleId = roleId;
      newState.permissionId = permissionId;
      newState.subPermissionId = subPermissionId;
      newState.permissionState = state;
      await this.rolePermissionStateRepository.save(newState);
    }
  }

  private async mapRoleToResponseDto(role: RoleEntity): Promise<RoleResponseDto> {
    const permissions = await this.getPermissionsByRoleId(role.id);
    const subPermissions = await this.getSubPermissionsByRoleId(role.id);

    return {
      id: role.id,
      name: role.name,
      roleType: role.roleType,
      isCustom: role.isCustom,
      priority: role.priority,
      clientId: role.client?.id,
      permissions,
      subPermissions,
      userCount: role.users?.length || 0,
      createdAt: role.createdAt,
      updatedAt: role.updatedAt,
    };
  }
}
