package io.aiven.klaw.service;

import static io.aiven.klaw.error.KlawErrorMessages.ROLE_PRM_ERR_101;

import io.aiven.klaw.config.ManageDatabase;
import io.aiven.klaw.dao.KwRolesPermissions;
import io.aiven.klaw.error.KlawException;
import io.aiven.klaw.helpers.KwConstants;
import io.aiven.klaw.model.ApiResponse;
import io.aiven.klaw.model.enums.ApiResultStatus;
import io.aiven.klaw.model.enums.PermissionType;
import io.aiven.klaw.model.requests.KwRolesPermissionsModel;
import java.util.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class RolesPermissionsControllerService {

  private final CommonUtilsService commonUtilsService;

  final ManageDatabase manageDatabase;

  private final MailUtils mailService;

  @Value("${klaw.installation.type:onpremise}")
  private String kwInstallationType;

  public RolesPermissionsControllerService(
      CommonUtilsService commonUtilsService, ManageDatabase manageDatabase, MailUtils mailService) {
    this.commonUtilsService = commonUtilsService;
    this.manageDatabase = manageDatabase;
    this.mailService = mailService;
  }

  private String getUserName() {
    return mailService.getUserName(commonUtilsService.getPrincipal());
  }

  public List<String> getRoles() {
    int tenantId = commonUtilsService.getTenantId(getUserName());
    try {
      if (commonUtilsService.isNotAuthorizedUser(
          commonUtilsService.getPrincipal(), PermissionType.FULL_ACCESS_USERS_TEAMS_ROLES))
        return Arrays.asList(
            manageDatabase.getKwPropertyValue("klaw.adduser.roles", tenantId).split(","));
      else {
        return new ArrayList<>(manageDatabase.getRolesPermissionsPerTenant(tenantId).keySet());
      }
    } catch (Exception e) {
      log.error("Exception:", e);
      return Arrays.asList(
          manageDatabase.getKwPropertyValue("klaw.adduser.roles", tenantId).split(","));
    }
  }

  public List<String> getRolesFromDb() {
    return new ArrayList<>(
        manageDatabase
            .getRolesPermissionsPerTenant(commonUtilsService.getTenantId(getUserName()))
            .keySet());
  }

  public Map<String, List<Map<String, Boolean>>> getPermissions(boolean isExternalCall) {

    if (isExternalCall
        && commonUtilsService.isNotAuthorizedUser(
            commonUtilsService.getPrincipal(), PermissionType.UPDATE_PERMISSIONS)) {
      return null;
    }

    PermissionType[] listPerms = PermissionType.values();
    List<String> permsList = new ArrayList<>();
    for (PermissionType permissionType : listPerms) {
      permsList.add(permissionType.name());
    }

    Collections.sort(permsList);

    Map<String, Set<String>> existingPerms =
        manageDatabase.getRolesPermissionsPerTenant(commonUtilsService.getTenantId(getUserName()));
    Map<String, List<Map<String, Boolean>>> finalMap = new HashMap<>();
    List<Map<String, Boolean>> mapList;
    List<String> roles = new ArrayList<>(existingPerms.keySet());
    Collections.sort(roles);

    for (String role : roles) {
      mapList = new ArrayList<>();
      for (String perm : permsList) {
        Map<String, Boolean> perMap = new HashMap<>();
        perMap.put(perm, existingPerms.get(role).contains(perm));
        mapList.add(perMap);
      }
      finalMap.put(role, mapList);
    }

    return finalMap;
  }

  public Map<String, String> getPermissionDescriptions() {
    Map<String, String> hMap = new HashMap<>();
    for (PermissionType value : PermissionType.values()) {
      hMap.put(value.name(), value.getDescription());
    }
    return hMap;
  }

  public ApiResponse updatePermissions(KwRolesPermissionsModel[] permissionsSet)
      throws KlawException {
    if (commonUtilsService.isNotAuthorizedUser(
        commonUtilsService.getPrincipal(), PermissionType.UPDATE_PERMISSIONS)) {
      return ApiResponse.NOT_AUTHORIZED;
    }

    Map<String, String> uniqueMap = new HashMap<>();
    for (KwRolesPermissionsModel kwRolesPermissionsModel : permissionsSet) {
      uniqueMap.put(
          kwRolesPermissionsModel.getRolePermission(),
          kwRolesPermissionsModel.getPermissionEnabled());
    }

    List<KwRolesPermissions> kwRolesPermissionsAdd = new ArrayList<>();
    List<KwRolesPermissions> kwRolesPermissionsDelete = new ArrayList<>();
    KwRolesPermissions tmpKwRolePermModel;

    String delimiter = "-----";
    int indexOfDelimiter;
    String isPermEnabled;
    int tenantId = commonUtilsService.getTenantId(getUserName());

    try {
      for (String permKey : uniqueMap.keySet()) {
        isPermEnabled = uniqueMap.get(permKey);
        indexOfDelimiter = permKey.indexOf(delimiter);

        tmpKwRolePermModel = new KwRolesPermissions();
        tmpKwRolePermModel.setRoleId(permKey.substring(0, indexOfDelimiter));
        tmpKwRolePermModel.setPermission(permKey.substring(indexOfDelimiter + 5));
        tmpKwRolePermModel.setTenantId(tenantId);

        if ("true".equals(isPermEnabled)) {
          kwRolesPermissionsAdd.add(tmpKwRolePermModel);
        } else if ("false".equals(isPermEnabled)) {
          kwRolesPermissionsDelete.add(tmpKwRolePermModel);
        }
      }

      if (kwRolesPermissionsAdd.size() > 0) {
        manageDatabase.getHandleDbRequests().updatePermissions(kwRolesPermissionsAdd, "ADD");
      }
      if (kwRolesPermissionsDelete.size() > 0) {
        manageDatabase.getHandleDbRequests().updatePermissions(kwRolesPermissionsDelete, "DELETE");
      }

      if (kwRolesPermissionsAdd.size() > 0 || kwRolesPermissionsDelete.size() > 0) {
        manageDatabase.loadRolesForAllTenants();
      }
      return ApiResponse.SUCCESS;
    } catch (Exception e) {
      log.error(e.getMessage());
      throw new KlawException(e.getMessage());
    }
  }

  public ApiResponse deleteRole(String roleId) throws KlawException {
    if (commonUtilsService.isNotAuthorizedUser(
        commonUtilsService.getPrincipal(), PermissionType.ADD_EDIT_DELETE_ROLES)) {
      return ApiResponse.NOT_AUTHORIZED;
    }

    if (KwConstants.USER_ROLE.equals(roleId) || KwConstants.SUPERADMIN_ROLE.equals(roleId)) {
      return ApiResponse.notOk(ROLE_PRM_ERR_101);
    }

    try {
      String status =
          manageDatabase
              .getHandleDbRequests()
              .deleteRole(roleId, commonUtilsService.getTenantId(getUserName()));
      manageDatabase.loadRolesForAllTenants();
      return ApiResultStatus.SUCCESS.value.equals(status)
          ? ApiResponse.ok(status)
          : ApiResponse.notOk(status);
    } catch (Exception e) {
      log.error(e.getMessage());
      throw new KlawException(e.getMessage());
    }
  }

  public ApiResponse addRoleId(String roleId) throws KlawException {
    if (commonUtilsService.isNotAuthorizedUser(
        commonUtilsService.getPrincipal(), PermissionType.ADD_EDIT_DELETE_ROLES)) {
      return ApiResponse.NOT_AUTHORIZED;
    }

    List<KwRolesPermissions> kwRolesPermissionsAdd = new ArrayList<>();
    KwRolesPermissions tmpKwRolePermModel = new KwRolesPermissions();
    tmpKwRolePermModel.setRoleId(roleId.trim().toUpperCase());
    tmpKwRolePermModel.setPermission(PermissionType.VIEW_TOPICS.name());
    tmpKwRolePermModel.setTenantId(commonUtilsService.getTenantId(getUserName()));

    kwRolesPermissionsAdd.add(tmpKwRolePermModel);

    try {
      String status =
          manageDatabase.getHandleDbRequests().updatePermissions(kwRolesPermissionsAdd, "ADD");
      manageDatabase.loadRolesForAllTenants();
      return ApiResultStatus.SUCCESS.value.equals(status)
          ? ApiResponse.ok(status)
          : ApiResponse.notOk(status);
    } catch (Exception e) {
      log.error(e.getMessage());
      throw new KlawException(e.getMessage());
    }
  }

  protected Set<String> getApproverRoles(String requestType, int tenantId) {
    Map<String, List<Map<String, Boolean>>> existingPermissions = getPermissions(false);
    Set<String> approverRoles = new HashSet<>();
    for (Map.Entry<String, List<Map<String, Boolean>>> permissionsListEntry :
        existingPermissions.entrySet()) {
      List<Map<String, Boolean>> entryDets = permissionsListEntry.getValue();
      for (Map<String, Boolean> entryDet : entryDets) {
        for (Map.Entry<String, Boolean> stringBooleanEntry : entryDet.entrySet()) {
          if (Boolean.TRUE.equals(stringBooleanEntry.getValue())) {
            if ("SUBSCRIPTIONS".equals(requestType)
                && PermissionType.APPROVE_SUBSCRIPTIONS
                    .name()
                    .equals(stringBooleanEntry.getKey())) {
              approverRoles.add(permissionsListEntry.getKey());
              break;
            } else if ("TOPICS".equals(requestType)
                && PermissionType.APPROVE_TOPICS.name().equals(stringBooleanEntry.getKey())) {
              approverRoles.add(permissionsListEntry.getKey());
              break;
            } else if ("SCHEMAS".equals(requestType)
                && PermissionType.APPROVE_SCHEMAS.name().equals(stringBooleanEntry.getKey())) {
              approverRoles.add(permissionsListEntry.getKey());
              break;
            } else if ("CONNECTORS".equals(requestType)
                && PermissionType.APPROVE_SCHEMAS.name().equals(stringBooleanEntry.getKey())) {
              approverRoles.add(permissionsListEntry.getKey());
              break;
            }
          }
        }
      }
    }

    return approverRoles;
  }
}
