import db from '../../config/database';
import { logger } from '../../utils/logger';
import { RBACEngine, Permission, DataScope, UserRoleAssignment } from '../../core/auth/RBACEngine';

export enum Role {
  ADMIN = 'ADMIN',
  MANAGER = 'MANAGER',
  OPERATOR = 'OPERATOR',
  FINANCE = 'FINANCE',
  SOURCING = 'SOURCING',
  LOGISTICS = 'LOGISTICS',
  ANALYST = 'ANALYST'
}

export interface RolePermission {
  role: Role;
  permission: string;
}

export interface UserRole {
  userId: string;
  role: Role;
  tenantId: string;
  shopId?: string;
  departmentId?: string;
}

export interface RBACCheckResult {
  allowed: boolean;
  role: Role;
  permission: string;
  resource: string;
  action: string;
}

export class RBACService {
  private static readonly USER_ROLES_TABLE = 'cf_user_roles';

  static async initTable(): Promise<void> {
    await RBACEngine.initTable();
    logger.info('✅ RBACService initialized with RBACEngine');
  }

  static async checkPermission(
    userId: string,
    resource: string,
    action: string,
    tenantId: string,
    shopId?: string
  ): Promise<RBACCheckResult> {
    logger.info(`[RBACService] Checking permission`, {
      userId,
      resource,
      action,
      tenantId,
      shopId
    });

    const permissionId = `${resource}:${action}`;
    const allowed = await RBACEngine.authorize(userId, tenantId, permissionId);
    
    const roles = await RBACEngine.getUserRoles(userId, tenantId);
    const primaryRole = roles[0] as Role || Role.OPERATOR;

    return {
      allowed,
      role: primaryRole,
      permission: permissionId,
      resource,
      action
    };
  }

  static async assignRole(
    userId: string,
    role: Role,
    tenantId: string,
    shopId?: string,
    departmentId?: string
  ): Promise<boolean> {
    logger.info(`[RBACService] Assigning role: ${role} to user: ${userId}`, {
      tenantId,
      shopId,
      departmentId
    });

    const assignment: UserRoleAssignment = {
      userId,
      tenantId,
      roleCode: role,
      shopId,
      departmentId
    };

    return RBACEngine.assignRole(assignment);
  }

  static async removeRole(
    userId: string,
    role: Role,
    tenantId: string
  ): Promise<boolean> {
    logger.info(`[RBACService] Removing role: ${role} from user: ${userId}`, {
      tenantId
    });

    return RBACEngine.removeRole(userId, tenantId, role);
  }

  static async getUserRoles(userId: string, tenantId: string): Promise<Role[]> {
    const roles = await RBACEngine.getUserRoles(userId, tenantId);
    return roles as Role[];
  }

  static async getUserPermissions(userId: string, tenantId: string): Promise<string[]> {
    return RBACEngine.getUserPermissions(userId, tenantId);
  }

  static async getDataScope(userId: string, tenantId: string): Promise<DataScope> {
    return RBACEngine.getDataScope(userId, tenantId);
  }

  static async hasPermission(userId: string, tenantId: string, permission: string): Promise<boolean> {
    return RBACEngine.authorize(userId, tenantId, permission);
  }

  static async hasAnyPermission(
    userId: string,
    tenantId: string,
    permissions: string[]
  ): Promise<boolean> {
    return RBACEngine.hasAnyPermission(userId, tenantId, permissions);
  }

  static async hasAllPermissions(
    userId: string,
    tenantId: string,
    permissions: string[]
  ): Promise<boolean> {
    return RBACEngine.hasAllPermissions(userId, tenantId, permissions);
  }

  static async authorize(
    userId: string,
    resource: string,
    action: string,
    tenantId: string,
    shopId?: string
  ): Promise<boolean> {
    const result = await this.checkPermission(userId, resource, action, tenantId, shopId);
    return result.allowed;
  }

  static getPermissionDefinitions(): Permission[] {
    return RBACEngine.getPermissionDefinitions();
  }

  static getRoleDefinitions(): Record<string, string[]> {
    return RBACEngine.getRoleDefinitions();
  }

  static async getRolePermissions(role: Role): Promise<string[]> {
    const roleDefs = this.getRoleDefinitions();
    return roleDefs[role] || [];
  }

  static async updateUserRoleScope(
    userId: string,
    tenantId: string,
    roleCode: string,
    departmentId?: string,
    shopId?: string
  ): Promise<boolean> {
    try {
      await db(this.USER_ROLES_TABLE)
        .where({
          user_id: userId,
          tenant_id: tenantId,
          role_code: roleCode
        })
        .update({
          department_id: departmentId,
          shop_id: shopId,
          updated_at: new Date()
        });
      
      logger.info(`[RBACService] Updated role scope for user: ${userId}`, {
        tenantId,
        roleCode,
        departmentId,
        shopId
      });
      
      return true;
    } catch (err: unknown) {
      const errorMessage = err instanceof Error ? err.message : 'Unknown error';
      logger.error(`[RBACService] Failed to update role scope: ${errorMessage}`);
      return false;
    }
  }

  static async getUsersByRole(
    tenantId: string,
    roleCode: string
  ): Promise<Array<{ userId: string; departmentId?: string; shopId?: string }>> {
    const userRoles = await db(this.USER_ROLES_TABLE)
      .where({
        tenant_id: tenantId,
        role_code: roleCode
      })
      .select('user_id', 'department_id', 'shop_id');

    return userRoles.map(ur => ({
      userId: ur.user_id,
      departmentId: ur.department_id,
      shopId: ur.shop_id
    }));
  }

  static async validateRoleTransition(
    userId: string,
    tenantId: string,
    fromRole: Role,
    toRole: Role
  ): Promise<{ valid: boolean; reason?: string }> {
    const roleHierarchy: Record<string, number> = {
      'ADMIN': 7,
      'MANAGER': 6,
      'FINANCE': 5,
      'SOURCING': 4,
      'LOGISTICS': 3,
      'OPERATOR': 2,
      'ANALYST': 1
    };

    const fromLevel = roleHierarchy[fromRole] || 0;
    const toLevel = roleHierarchy[toRole] || 0;

    if (toLevel > fromLevel) {
      return {
        valid: false,
        reason: 'Cannot assign higher role without proper authorization'
      };
    }

    return { valid: true };
  }
}

export default RBACService;
