package ru.micord.ervu.account_applications.component.service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;


import database.dao.DefaultLoadDao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import ru.micord.ervu.account_applications.component.model.TreeItemDto;
import ru.micord.ervu.account_applications.component.rpc.TreeItemRpcService;
import ru.micord.ervu.account_applications.security.context.SecurityContext;
import ru.micord.ervu.account_applications.security.model.role.ErvuPermissionAuthority;

import ru.cg.webbpm.modules.database.api.bean.TableRow;
import ru.cg.webbpm.modules.database.api.dao.option.LoadOptions;
import ru.cg.webbpm.modules.database.bean.annotation.GraphSource;
import ru.cg.webbpm.modules.database.bean.annotation.TypedColumn;
import ru.cg.webbpm.modules.database.bean.entity_graph.EntityColumn;
import ru.cg.webbpm.modules.database.bean.entity_graph.EntityColumnType;
import ru.cg.webbpm.modules.standard_annotations.validation.NotNull;

/**
 * @author r.latypov
 */
@Service
public class TreeItemService {
  private final SecurityContext securityContext;
  @NotNull
  public DefaultLoadDao loadDao;
  @GraphSource(value = TreeItemRpcService.class, scanMode = GraphSource.ScanMode.SELF)
  @NotNull
  public EntityColumn idColumn;
  @GraphSource(value = TreeItemRpcService.class, scanMode = GraphSource.ScanMode.SELF)
  @NotNull
  public EntityColumn parentIdColumn;
  @GraphSource(value = TreeItemRpcService.class, scanMode = GraphSource.ScanMode.SELF)
  @TypedColumn(colTypes = EntityColumnType.STRING)
  @NotNull
  public EntityColumn labelColumn;
  @GraphSource(value = TreeItemRpcService.class, scanMode = GraphSource.ScanMode.SELF)
  public EntityColumn businessIdColumn;
  @GraphSource(value = TreeItemRpcService.class, scanMode = GraphSource.ScanMode.SELF)
  public EntityColumn domainIdColumn;
  @Value("${ervu.security.role:security_administrator}")
  private String ervuSecurityRole;

  public TreeItemService(SecurityContext securityContext) {
    this.securityContext = securityContext;
  }

  public List<TreeItemDto> loadTreeData() {
    String domainId = securityContext.getDomainId();
    Set<ErvuPermissionAuthority> permissions = securityContext.getPermissions();

    List<TreeItemDto> filteredTreeItems = loadTreeItems();
    if (ervuSecurityRole == null || permissions.stream().noneMatch(permission -> permission.getAuthority().equals(
        ervuSecurityRole))) {
      filteredTreeItems = filteredTreeItems.stream()
          .filter(item -> item.domainId.equalsIgnoreCase(domainId))
          .toList();
    }
    else {
      filteredTreeItems = filteredTreeItems.stream()
          .filter(item -> item.parentId == null)
          .toList();
    }
    filteredTreeItems.forEach(this::setDomainIdToNull);
    return filteredTreeItems;
  }

  private void setDomainIdToNull(TreeItemDto treeItem) {
    treeItem.domainId = null;
    TreeItemDto[] treeItemChildren = treeItem.children;
    if (treeItemChildren != null && treeItemChildren.length > 0) {
      for (TreeItemDto child : treeItemChildren) {
        setDomainIdToNull(child);
      }
    }
  }
  
  private List<TreeItemDto> loadTreeItems() {
    List<TreeItemDto> loadedList = this.loadDao.load(getColumns(), new LoadOptions()).stream()
        .map(this::toTreeItemDto)
        .toList();

    Map<Object, List<TreeItemDto>> childrenMap = new HashMap<>();
    loadedList.forEach(item -> {
      Object parentId = item.parentId;
      if (parentId != null) {
        List<TreeItemDto> listFromMap = childrenMap.computeIfAbsent(parentId,
            k -> new ArrayList<>()
        );
        listFromMap.add(item);
      }
    });

    loadedList.forEach(item -> {
      List<TreeItemDto> children = childrenMap.get(item.id);
      if (children != null) {
        item.children = children.toArray(new TreeItemDto[0]);
      }
    });
    return loadedList;
  }

  private Set<EntityColumn> getColumns() {
    Set<EntityColumn> columnSet = new HashSet<>();
    columnSet.add(idColumn);
    columnSet.add(parentIdColumn);
    columnSet.add(labelColumn);
    if (businessIdColumn != null) {
      columnSet.add(businessIdColumn);
    }
    if (domainIdColumn != null) {
      columnSet.add(domainIdColumn);
    }
    return columnSet;
  }

  private TreeItemDto toTreeItemDto(TableRow tableRow) {
    TreeItemDto treeItemDto = new TreeItemDto(
        tableRow.get(idColumn), tableRow.get(parentIdColumn), (String) tableRow.get(labelColumn)
    );
    if (businessIdColumn != null) {
      treeItemDto.businessId = tableRow.get(businessIdColumn);
    }
    if (domainIdColumn != null) {
      treeItemDto.domainId = (String) tableRow.get(domainIdColumn);
    }
    return treeItemDto;
  }
}
