package eu.eudat.logic.managers;

import eu.eudat.data.dao.criteria.FunderCriteria;
import eu.eudat.data.dao.criteria.GrantCriteria;
import eu.eudat.data.dao.criteria.ProjectCriteria;
import eu.eudat.data.entities.*;
import eu.eudat.elastic.entities.Dmp;
import eu.eudat.logic.mapper.elastic.DmpMapper;
import eu.eudat.logic.services.ApiContext;
import eu.eudat.logic.services.operations.DatabaseRepository;
import eu.eudat.models.data.dmp.DataManagementPlan;
import eu.eudat.models.data.security.Principal;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.text.ParseException;
import java.util.UUID;

@Component
public class QuickWizardManager {

    private ApiContext apiContext;
    private DatabaseRepository databaseRepository;
    private DatasetManager datasetManager;

    @Autowired
    public QuickWizardManager(ApiContext apiContext, DatasetManager datasetManager) {
        this.apiContext = apiContext;
        this.databaseRepository = apiContext.getOperationsContext().getDatabaseRepository();
        this.datasetManager = datasetManager;
    }

    public Funder createOrUpdate(eu.eudat.models.data.funder.Funder funder, Principal principal) {
        Funder funderEntity = funder.toDataModel();
        return databaseRepository.getFunderDao().createOrUpdate(funderEntity);
    }

    public Grant createOrUpdate(eu.eudat.models.data.grant.Grant grant, Principal principal) throws ParseException, IOException {
        eu.eudat.data.entities.Grant grantEntity = grant.toDataModel();
        grantEntity.setType(eu.eudat.data.entities.Grant.GrantType.INTERNAL.getValue());
        grantEntity.setCreationUser(databaseRepository.getUserInfoDao().find(principal.getId()));
        return databaseRepository.getGrantDao().createOrUpdate(grantEntity);
    }

    public Project createOrUpdate(eu.eudat.models.data.project.Project project, Principal principal) {
        Project projectEntity = project.toDataModel();
        projectEntity.setCreationUser(databaseRepository.getUserInfoDao().find(principal.getId()));
        return databaseRepository.getProjectDao().createOrUpdate(projectEntity);
    }

    public DMP createOrUpdate(DataManagementPlan dataManagementPlan, Funder funderEntity, Principal principal) throws Exception {
        DMP newDmp = dataManagementPlan.toDataModel();
        if (funderEntity != null) {
            newDmp.getGrant().setFunder(funderEntity);
        }
        UserInfo user = apiContext.getOperationsContext().getDatabaseRepository().getUserInfoDao().find(principal.getId());
        createFunderIfItDoesntExist(newDmp, user);
        createGrantIfItDoesntExist(newDmp, user);
        if (newDmp.getProject() == null) {
            newDmp.setProject(new Project());
            newDmp.setProject(newDmp.getProject().projectFromGrant(newDmp.getGrant()));
        }
        createProjectIfItDoesntExist(newDmp, user);

        apiContext.getOperationsContext().getDatabaseRepository().getGrantDao().createOrUpdate(newDmp.getGrant());
        DMP dmpret = apiContext.getOperationsContext().getDatabaseRepository().getDmpDao().createOrUpdate(newDmp);
        if (dataManagementPlan.getAssociatedUsers().size() == 0)
            assignUser(newDmp, user, apiContext);

        this.updateIndex(dmpret);

        return dmpret;
    }

    private void updateIndex(DMP dmp) throws IOException {
        DmpMapper mapper = new DmpMapper(apiContext, datasetManager);
        Dmp elastic = mapper.toElastic(dmp);
        apiContext.getOperationsContext().getElasticRepository().getDmpRepository().createOrUpdate(elastic);
    }

    private void assignUser(DMP dmp, UserInfo userInfo, ApiContext apiContext) {
        UserDMP userDMP = new UserDMP();
        userDMP.setDmp(dmp);
        userDMP.setUser(userInfo);
        userDMP.setRole(UserDMP.UserDMPRoles.OWNER.getValue());
        apiContext.getOperationsContext().getDatabaseRepository().getUserDmpDao().createOrUpdate(userDMP);
    }

    private void createGrantIfItDoesntExist(DMP newDmp, UserInfo userInfo) {
        if (newDmp.getGrant() != null) {
            Grant grant = newDmp.getGrant();
            GrantCriteria criteria = new GrantCriteria();
            criteria.setReference(grant.getReference());
            eu.eudat.data.entities.Grant grantEntity = databaseRepository.getGrantDao().getWithCriteria(criteria).getSingleOrDefault();
            if (grantEntity != null) grant.setId(grantEntity.getId());
            else {
                grant.setType(Grant.GrantType.EXTERNAL.getValue());
                databaseRepository.getGrantDao().createOrUpdate(grant);
            }
        }
    }

    private void createFunderIfItDoesntExist(DMP newDmp, UserInfo userInfo) {
        if (newDmp.getGrant().getFunder() != null) {
            Funder funder = newDmp.getGrant().getFunder();
            FunderCriteria criteria = new FunderCriteria();
            criteria.setReference(funder.getReference());
            eu.eudat.data.entities.Funder funderEntity = databaseRepository.getFunderDao().getWithCritetia(criteria).getSingleOrDefault();
            if (funderEntity != null) funder.setId(funderEntity.getId());
            else {
                funder.setType(Funder.FunderType.EXTERNAL.getValue());
                databaseRepository.getFunderDao().createOrUpdate(funder);
            }
        }
    }

    private void createProjectIfItDoesntExist(DMP newDmp, UserInfo userInfo) {
        if (newDmp.getProject() != null) {
            Project project = newDmp.getProject();
            ProjectCriteria criteria = new ProjectCriteria();
            criteria.setReference(project.getReference());
            eu.eudat.data.entities.Project projectEntity = databaseRepository.getProjectDao().getWithCritetia(criteria).getSingleOrDefault();
            if (projectEntity != null) project.setId(projectEntity.getId());
            else {
                if (project.getId() == null) project.setId(UUID.randomUUID());
                project.setType(Project.ProjectType.EXTERNAL.getValue());
                databaseRepository.getProjectDao().createOrUpdate(project);
            }
        }
    }
}
