import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@mikro-orm/nestjs';
import { EntityManager, EntityRepository } from '@mikro-orm/core';
import { Permission } from '../entities/permission.entity';
import { CacheService } from 'src/modules/utils/cache.service';
import { AdminRepository } from 'src/modules/admin/repositories/admin.repository';
import { AdminRole } from 'src/modules/admin/entities/adminRole.entity';

@Injectable()
export class PermissionsService {
  private readonly ADMIN_PERMISSIONS_KEY = 'admin-perms';

  constructor(
    @InjectRepository(Permission)
    private readonly permissionRepository: EntityRepository<Permission>,
    private readonly cacheService: CacheService,
    private readonly adminRepository: AdminRepository,
    private readonly em: EntityManager,
  ) { }

  async findAll() {
    const permissions = await this.permissionRepository.findAll();
    return permissions;
  }

  /**
   * Get admin permissions from cache or database
   * @param adminId - The admin ID
   * @param restId - The restaurant ID
   * @returns Array of permission names (string[])
   */
  async getAdminPermissions(adminId: string, restId: string): Promise<string[]> {
    const cacheKey = `${this.ADMIN_PERMISSIONS_KEY}:${adminId}:${restId}`;

    // Try to get from cache first
    const cachedPermissions = await this.cacheService.get<string>(cacheKey);
    if (cachedPermissions) {
      try {
        const parsed: unknown = JSON.parse(cachedPermissions);
        // Ensure it's an array of strings
        if (Array.isArray(parsed) && parsed.every((p): p is string => typeof p === 'string')) {
          return parsed;
        }
        // If invalid format, continue to fetch from DB
      } catch {
        // If parsing fails, continue to fetch from DB
      }
    }

    // If not in cache, fetch from database
    const admin = await this.adminRepository.findOne(
      { id: adminId, roles: { restaurant: { id: restId } } },
      { populate: ['roles', 'roles.role', 'roles.role.permissions'] },
    );

    if (!admin || !admin.roles) {
      return [];
    }

    // Ensure roles collection is loaded
    await admin.roles.loadItems();

    // Extract permission names as array of strings
    const permissions = await Promise.all(
      admin.roles
        .getItems()
        .filter(r => r.role) // Filter out any null/undefined roles
        .map(async r => {
          // Ensure permissions collection is initialized
          if (!r.role.permissions.isInitialized()) {
            await r.role.permissions.loadItems();
          }
          return r.role.permissions.getItems();
        }),
    );

    return permissions.flat().map(p => p.name);
  }

  async getAdminFullPermissions(adminId: string, restId: string): Promise<Permission[]> {
    const listOfPermissions = []
    const adminRoles = await this.em.findOne(AdminRole, {
      admin: {
        id: adminId,
      },
      restaurant: {
        id: restId,
      },
    }, { populate: ['role', 'role.permissions'] });
    if (adminRoles) {
      listOfPermissions.push(...adminRoles.role.permissions.getItems());
    }
    return listOfPermissions
  }

  /**
   * Invalidate admin permissions cache
   * @param adminId - The admin ID
   * @param restId - The restaurant ID
   */
  async invalidateAdminPermissionsCache(adminId: string, restId: string): Promise<void> {
    const cacheKey = `${this.ADMIN_PERMISSIONS_KEY}:${adminId}:${restId}`;
    await this.cacheService.del(cacheKey);
  }
}
