package eu.eudat.logic.managers;

import eu.eudat.data.dao.entities.OrganisationDao;
import eu.eudat.data.query.items.table.organisations.OrganisationsTableRequest;
import eu.eudat.logic.builders.model.models.OrganisationBuilder;
import eu.eudat.logic.proxy.config.ExternalUrlCriteria;
import eu.eudat.logic.proxy.config.exceptions.HugeResultSet;
import eu.eudat.logic.proxy.config.exceptions.NoURLFound;
import eu.eudat.logic.services.ApiContext;
import eu.eudat.logic.services.operations.DatabaseRepository;
import eu.eudat.models.data.dmp.Organisation;
import eu.eudat.models.data.external.ExternalSourcesItemModel;
import eu.eudat.models.data.external.OrganisationsExternalSourcesModel;
import eu.eudat.models.data.helpers.common.DataTableData;
import eu.eudat.models.data.security.Principal;
import eu.eudat.queryable.QueryableList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Component
public class OrganisationsManager {

    private ApiContext apiContext;
    private DatabaseRepository databaseRepository;

    @Autowired
    public OrganisationsManager(ApiContext apiContext) {
        this.apiContext = apiContext;
        this.databaseRepository = apiContext.getOperationsContext().getDatabaseRepository();
    }

    public DataTableData<Organisation> getPagedOrganisations(OrganisationsTableRequest organisationsTableRequest, Principal principal) throws Exception {
        eu.eudat.data.entities.UserInfo userInfo = new eu.eudat.data.entities.UserInfo();
        userInfo.setId(principal.getId());
        OrganisationDao organisationDao = databaseRepository.getOrganisationDao();

        QueryableList<eu.eudat.data.entities.Organisation> items = organisationDao.getWithCriteria(organisationsTableRequest.getCriteria());
        QueryableList<eu.eudat.data.entities.Organisation> authItems = organisationDao.getAuthenticated(items, userInfo);
        QueryableList<eu.eudat.data.entities.Organisation> pagedItems = PaginationManager.applyPaging(authItems, organisationsTableRequest);

        List<Organisation> org = pagedItems.toList().stream().distinct().map(item -> new Organisation().fromDataModel(item)).collect(Collectors.toList());
        DataTableData<Organisation> organisationDataTableData = new DataTableData<>();
        organisationDataTableData.setData(org);
        organisationDataTableData.setTotalCount(pagedItems.count());

        return organisationDataTableData;
    }

    public DataTableData<Organisation> getPublicPagedOrganisations(OrganisationsTableRequest organisationsTableRequest) throws Exception {
        organisationsTableRequest.getCriteria().setPublic(true);
        OrganisationDao organisationDao = databaseRepository.getOrganisationDao();

        QueryableList<eu.eudat.data.entities.Organisation> items = organisationDao.getWithCriteria(organisationsTableRequest.getCriteria());
        QueryableList<eu.eudat.data.entities.Organisation> pagedItems = PaginationManager.applyPaging(items, organisationsTableRequest);

        List<Organisation> org = pagedItems.toList().stream().distinct().map(item -> new Organisation().fromDataModel(item)).collect(Collectors.toList());
        DataTableData<Organisation> organisationDataTableData = new DataTableData<>();
        organisationDataTableData.setData(org);
        organisationDataTableData.setTotalCount(pagedItems.count());

        return organisationDataTableData;
    }

    public List<Organisation> getWithExternal(OrganisationsTableRequest organisationsTableRequest, Principal principal) throws Exception {
        eu.eudat.data.entities.UserInfo userInfo = new eu.eudat.data.entities.UserInfo();
        userInfo.setId(principal.getId());
        OrganisationDao organisationDao = databaseRepository.getOrganisationDao();

        QueryableList<eu.eudat.data.entities.Organisation> items = organisationDao.getWithCriteria(organisationsTableRequest.getCriteria());
        QueryableList<eu.eudat.data.entities.Organisation> authItems = organisationDao.getAuthenticated(items, userInfo);
        QueryableList<eu.eudat.data.entities.Organisation> pagedItems = PaginationManager.applyPaging(authItems, organisationsTableRequest);

        List<Organisation> org = pagedItems.toList().stream().distinct().map(item -> new Organisation().fromDataModel(item)).collect(Collectors.toList());

        ExternalUrlCriteria externalUrlCriteria = new ExternalUrlCriteria(organisationsTableRequest.getCriteria().getLabelLike());
        List<Map<String, String>> remoteRepos = apiContext.getOperationsContext().getRemoteFetcher().getOrganisations(externalUrlCriteria, null);
        OrganisationsExternalSourcesModel organisationsExternalSourcesModel = new OrganisationsExternalSourcesModel().fromExternalItem(remoteRepos);
        for (ExternalSourcesItemModel externalListingItem : organisationsExternalSourcesModel) {
            Organisation organisation = apiContext.getOperationsContext().getBuilderFactory().getBuilder(OrganisationBuilder.class)
                    .name(externalListingItem.getName())
                    .reference(externalListingItem.getRemoteId())
                    .tag(externalListingItem.getTag())
                    .key(externalListingItem.getKey())
                    .build();
            org.add(organisation);
        }
        return org;
    }

    public List<Organisation> getCriteriaWithExternal(String query, String type) throws HugeResultSet, NoURLFound {
        ExternalUrlCriteria externalUrlCriteria = new ExternalUrlCriteria(query);
        List<Map<String, String>> remoteRepos = apiContext.getOperationsContext().getRemoteFetcher().getOrganisations(externalUrlCriteria, type);
        OrganisationsExternalSourcesModel organisationsExternalSourcesModel = new OrganisationsExternalSourcesModel().fromExternalItem(remoteRepos);
        List<Organisation> organisations = new LinkedList<>();
        for (ExternalSourcesItemModel externalListingItem : organisationsExternalSourcesModel) {
            Organisation organisation = apiContext.getOperationsContext().getBuilderFactory().getBuilder(OrganisationBuilder.class)
                    .name(externalListingItem.getName())
                    .reference(externalListingItem.getRemoteId())
                    .tag(externalListingItem.getTag())
                    .key(externalListingItem.getKey())
                    .build();
            organisations.add(organisation);
        }
        return organisations.stream().distinct().collect(Collectors.toList());
    }
}
