import { ForbiddenException, Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../infra/prisma.service';

export interface UserAccessContext {
  roles: string[];
  permissions: string[];
  canAccessAdmin: boolean;
  canManageRoles: boolean;
  canViewOAuth: boolean;
  canManageOAuth: boolean;
  canManageAllOAuth: boolean;
  canManageUsers: boolean;
  canManageAllUsers: boolean;
  canViewUsers: boolean;
  canManageSettings: boolean;
  canViewUserDocuments: boolean;
  canVerifyUsers: boolean;
  canManageBots: boolean;
}

function hasAnyPermission(permissions: string[], ...slugs: string[]) {
  return slugs.some((slug) => permissions.includes(slug));
}

@Injectable()
export class AccessService {
  constructor(private readonly prisma: PrismaService) {}

  async getUserAccess(userId: string, isSuperAdmin: boolean): Promise<UserAccessContext> {
    if (isSuperAdmin) {
      return {
        roles: ['super-admin'],
        permissions: [
          'admin.access',
          'oauth.view',
          'oauth.view.all',
          'oauth.manage',
          'oauth.manage.all',
          'users.view',
          'users.view.all',
          'users.manage',
          'users.manage.all',
          'settings.manage',
          'rbac.manage',
          'documents.view_others',
          'users.verify',
          'bots.manage.all'
        ],
        canAccessAdmin: true,
        canManageRoles: true,
        canViewOAuth: true,
        canManageOAuth: true,
        canManageAllOAuth: true,
        canManageUsers: true,
        canManageAllUsers: true,
        canViewUsers: true,
        canManageSettings: true,
        canViewUserDocuments: true,
        canVerifyUsers: true,
        canManageBots: true
      };
    }

    const [roleLinks, directLinks] = await Promise.all([
      this.prisma.userRole.findMany({
        where: { userId },
        include: {
          role: {
            include: {
              permissions: {
                include: { permission: true }
              }
            }
          }
        }
      }),
      this.prisma.userPermission.findMany({
        where: { userId },
        include: { permission: true }
      })
    ]);

    const roles = roleLinks.map((link) => link.role.slug);
    const rolePermissions = roleLinks.flatMap((link) => link.role.permissions.map((item) => item.permission.slug));
    const directPermissions = directLinks.map((link) => link.permission.slug);
    const permissions = [...new Set([...rolePermissions, ...directPermissions])];

    const canViewOAuth = hasAnyPermission(permissions, 'oauth.view', 'oauth.view.all', 'oauth.manage', 'oauth.manage.all');
    const canManageOAuth = hasAnyPermission(permissions, 'oauth.manage', 'oauth.manage.all');
    const canManageAllOAuth = permissions.includes('oauth.manage.all');
    const canViewUsers = hasAnyPermission(permissions, 'users.view', 'users.view.all', 'users.manage', 'users.manage.all', 'users.read');
    const canManageUsers = hasAnyPermission(permissions, 'users.manage', 'users.manage.all');
    const canManageAllUsers = permissions.includes('users.manage.all');

    return {
      roles,
      permissions,
      canAccessAdmin: permissions.includes('admin.access'),
      canManageRoles: permissions.includes('rbac.manage'),
      canViewOAuth,
      canManageOAuth,
      canManageAllOAuth,
      canManageUsers,
      canManageAllUsers,
      canViewUsers,
      canManageSettings: permissions.includes('settings.manage'),
      canViewUserDocuments: permissions.includes('documents.view_others'),
      canVerifyUsers: permissions.includes('users.verify'),
      canManageBots: permissions.includes('bots.manage.all')
    };
  }

  canManageOAuthClient(
    access: UserAccessContext,
    actorUserId: string,
    client: { createdById: string | null }
  ) {
    if (access.canManageAllOAuth) return true;
    if (access.canManageOAuth && client.createdById === actorUserId) return true;
    return false;
  }

  async assertRbacManage(actorUserId: string) {
    const actor = await this.prisma.user.findUnique({ where: { id: actorUserId } });
    if (!actor) {
      throw new NotFoundException('Пользователь не найден');
    }
    if (actor.isSuperAdmin) return;
    const access = await this.getUserAccess(actorUserId, false);
    if (!access.canManageRoles) {
      throw new ForbiddenException('Недостаточно прав для управления ролями и правами');
    }
  }

  async assertCanVerifyUsers(actorUserId: string) {
    const actor = await this.prisma.user.findUnique({ where: { id: actorUserId } });
    if (!actor) {
      throw new NotFoundException('Пользователь не найден');
    }
    if (actor.isSuperAdmin) return;
    const access = await this.getUserAccess(actorUserId, false);
    if (!access.canVerifyUsers) {
      throw new ForbiddenException('Недостаточно прав для верификации пользователей');
    }
  }

  async assertSuperAdmin(actorUserId: string) {
    const actor = await this.prisma.user.findUnique({ where: { id: actorUserId } });
    if (!actor?.isSuperAdmin) {
      throw new ForbiddenException('Только супер-администратор может выполнять это действие');
    }
  }

  async assertPermission(userId: string, isSuperAdmin: boolean, permission: string) {
    const access = await this.getUserAccess(userId, isSuperAdmin);
    if (!access.permissions.includes(permission) && !isSuperAdmin) {
      throw new Error('FORBIDDEN_PERMISSION');
    }
    return access;
  }

  async assertCanViewUserDocuments(requesterId: string, targetUserId: string) {
    if (requesterId === targetUserId) return;
    const requester = await this.prisma.user.findUnique({ where: { id: requesterId } });
    if (!requester) {
      throw new ForbiddenException('Пользователь не найден');
    }
    const access = await this.getUserAccess(requesterId, requester.isSuperAdmin);
    if (!access.canViewUserDocuments) {
      throw new ForbiddenException('Нет доступа к документам пользователя');
    }
  }
}
