package service.loading;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import component.field.dataconvert.DataConverter;
import component.field.dataconvert.DataConverterProvider;
import component.field.loading.FieldDefaultValue;
import component.field.loading.LoadType;
import model.FieldData;

import ru.cg.webbpm.modules.database.api.bean.TableRow;
import ru.cg.webbpm.modules.database.api.dao.LoadDao;
import ru.cg.webbpm.modules.database.bean.entity_graph.EntityColumn;

/**
 * @author kochetokov
 */
public class ProjectDefaultValueLoaderServiceImpl extends AbstractDefaultValueLoaderService
    implements DefaultValueLoaderService {

  public LoadDao loadDao;

  @Override
  public List<FieldData> loadFieldDefaultValues(List<String> guids) {
    return Stream.concat(
        loadValuesByFieldService(guids).stream(),
        loadValuesByColumn(guids).stream()
    ).collect(Collectors.toList());
  }

  protected List<FieldData> loadValuesByColumn(List<String> guids) {
    Map<String, FieldDefaultValue<?>> byColumnMapping = getScriptsMapping(guids,
        fieldDefaultValue -> fieldDefaultValue.loadType == LoadType.BY_COLUMN
    );
    Map<String, EntityColumn> idToColumnMapping = byColumnMapping.entrySet().stream()
        .collect(Collectors.toMap(
                Map.Entry::getKey,
                entry -> {
                  try {
                    return entry.getValue().getDefaultValueColumn();
                  }
                  catch (Exception e) {
                    throw new RuntimeException(
                        String.format("Cannot load values for object with id = %s",
                            entry.getKey()
                        ), e);
                  }
                }
            )
        );
    return loadData(idToColumnMapping);
  }

  protected List<FieldData> loadData(Map<String, EntityColumn> idToColumnMapping) {

    if (loadDao == null) {
      return Collections.emptyList();
    }
    Set<EntityColumn> columns = new HashSet<>(idToColumnMapping.values());
    Map<EntityColumn, Object> columnToDataMapping;

    List<TableRow> tableRows = loadDao.load(columns);

    if (tableRows.isEmpty()) {
      return Collections.emptyList();
    }

    if (tableRows.size() > 1) {
      throw new IllegalArgumentException("Default graph must return one row");
    }
    columnToDataMapping = tableRows.get(0).getColumnToDataMap();

    return idToColumnMapping.entrySet().stream()
        .map(entry -> {
          DataConverter dataConverter = DataConverterProvider.getDataConverter(
              entry.getValue().getType());
          Object convertedValue = dataConverter.convertValueForLoad(
              columnToDataMapping.get(entry.getValue()));
          return new FieldData(entry.getKey(), convertedValue);
        })
        .collect(Collectors.toList());
  }
}
