import 'package:logging/logging.dart';

import 'package:pshared/api/requests/change_role.dart';
import 'package:pshared/api/requests/permissions/change_policies.dart';
import 'package:pshared/api/responses/policies.dart';
import 'package:pshared/data/mapper/permissions/data/permissions.dart';
import 'package:pshared/data/mapper/permissions/descriptions/role.dart';
import 'package:pshared/data/mapper/permissions/descriptions/description.dart';
import 'package:pshared/models/permissions/descriptions/role.dart';
import 'package:pshared/models/permissions/access.dart';
import 'package:pshared/models/permissions/data/policy.dart';
import 'package:pshared/service/authorization/service.dart';
import 'package:pshared/service/services.dart';


class PermissionsService {
  static final _logger = Logger('service.permissions');
  static const String _objectType = Services.permission;

  static Future<UserAccess> load(String organizationRef) async {
    _logger.fine('Loading permissions...');
    return _getPolicies(AuthorizationService.getGETResponse(_objectType, organizationRef));
  }

   static Future<UserAccess> loadAll(String organizationRef) async {
    _logger.fine('Loading permissions for all the users...');
    return _getPolicies(AuthorizationService.getGETResponse(_objectType, '/all/$organizationRef'));
  } 

  static Future<void> changeRole(String organizationRef, ChangeRole request) async {
    _logger.fine('Changing role for account ${request.accountRef} to role ${request.newRoleDescriptionRef}');
    await AuthorizationService.getPOSTResponse(_objectType, '/change_role/$organizationRef', request.toJson());
  }  

  static Future<void> deleteRoleDescription(String roleDescriptionRef) async {
    _logger.fine('Deleting role $roleDescriptionRef...');
    await AuthorizationService.getDELETEResponse(_objectType, '/role/$roleDescriptionRef', {});
  }  

  static Future<void> createRoleDescription(RoleDescription roleDescription) async {
    _logger.fine('Creating role ${roleDescription.name}...');
    await AuthorizationService.getPOSTResponse(
      _objectType,
      '/role',
      roleDescription.toDTO().toJson(),
    );
  }

  static Future<void> createPolicies(List<Policy> policies) async {
    _logger.fine('Creating ${policies.length} policies...');
    await AuthorizationService.getPOSTResponse(
      _objectType, 
      '/policies', 
      PoliciesChangeRequest.add(policies: policies).toJson(),
    );
  }  

  static Future<void> deletePolicies(List<Policy> policies) async {
    _logger.fine('Deleting ${policies.length} policies...');
    await AuthorizationService.getDELETEResponse(
      _objectType, 
      '/policies', 
      PoliciesChangeRequest.remove(policies: policies).toJson(),
    );
  }  

  static Future<void> changePolicies(List<Policy> add, List<Policy> remove) async {
    final common = add.toSet().intersection(remove.toSet());
    if (common.isNotEmpty) {
      throw ArgumentError.value(common, 'add/remove', 'These policies are in both add and remove: ${common.toString()}');
    }    
    _logger.fine('Adding ${add.length} policies, removing ${remove.length} policies...');
    await AuthorizationService.getPUTResponse(
      _objectType, 
      '/policies', 
      PoliciesChangeRequest.change(add: add, remove: remove).toJson(),
    );
  }  

  static Future<UserAccess> _getPolicies(Future<Map<String, dynamic>> future) async {
    final resp = PoliciesResponse.fromJson(await future);
    final res = UserAccess(
      descriptions: resp.descriptions.toDomain(),
      permissions: resp.permissions.toDomain(),
    );
    _logger.fine('Loaded ${res.descriptions.roles.length} role descriptions, ${res.permissions.roles.length} role assignments, ${res.descriptions.policies.length} policy descriptions, ${res.permissions.policies.length} assigned policies, and ${res.permissions.permissions.length} assigned permissions');
    
    return res;
  }
}
