package eu.dnetlib.goldoa.service;

import eu.dnetlib.goldoa.domain.*;
import eu.dnetlib.goldoa.service.dao.BudgetDAO;
import eu.dnetlib.goldoa.service.utils.EmailUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ExecutorService;

/**
 * Created by antleb on 4/2/15.
 */
@Transactional
public class BudgetManagerImpl implements BudgetManager {

    @Autowired
    private BudgetDAO budgetDAO;
    @Autowired
    private PersonManager personManager;
    @Autowired
    private OrganizationManager organizationManager;
    @Autowired
    ExecutorService executorService;
    @Autowired
    private EmailUtils emailUtils;

    @Override
    public Budget saveBudget(final Budget budget) {
        if (budget.getId() == null) {
            budget.setId(UUID.randomUUID().toString());
            budget.setDate(new Date());
            budget.setRemaining(budget.getAmountGranted());
        }

        budgetDAO.saveBudget(budget);

        budgetDAO.deleteBudgetRelations(budget);
        budgetDAO.insertBudgetRelations(budget);

        if (Budget.Status.SUBMITTED.equals(getStatus(budget.getStatusCode()))) {
            executorService.submit(new Runnable() {
                @Override
                public void run() {
                    try {
                        BudgetInfo budgetInfo = getBudgetInfo(budget);
                        List<Person> moderators = personManager.getModerators();

                        emailUtils.sendUserNewBudgetEmail(budgetInfo);

                        for (Person moderator:moderators)
                            emailUtils.sendModeratorNewBudgetEmail(moderator, budgetInfo);

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
        }

        return budget;
    }

    @Override
    public BudgetInfo getBudget(String budgetId) {
        return getBudgetInfo(budgetDAO.getBudget(budgetId));
    }

    @Override
    public List<BudgetInfo> getBudgets() {
        List<BudgetInfo> res = new ArrayList<BudgetInfo>();

        for (Budget budget:budgetDAO.getBudgets())
            res.add(getBudgetInfo(budget));

        return res;
    }

    @Override
    public List<BudgetInfo> getBudgetsForUser(String userId) {
        List<BudgetInfo> res = new ArrayList<BudgetInfo>();

        for (Budget budget:budgetDAO.getBudgetsForUser(userId))
            res.add(getBudgetInfo(budget));

        return res;
    }

    @Override
    public List<BudgetInfo> getBudgetsForOrganization(List<String> organizationIds) {
        List<BudgetInfo> res = new ArrayList<BudgetInfo>();

        for (Budget budget:budgetDAO.getBudgetsForOrganization(organizationIds))
            res.add(getBudgetInfo(budget));

        return res;
    }

    @Override
    public void approveBudget(final String budgetId, String comment, float amountGranted) {
        budgetDAO.approveBudget(budgetId, amountGranted);
        budgetDAO.updateBudgetComment(budgetId, comment);

        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    BudgetInfo budgetInfo = getBudget(budgetId);
                    List<Person> moderators = personManager.getModerators();

                    emailUtils.sendUserAcceptedBudgetEmail(budgetInfo);

                    for (Person moderator : moderators)
                        emailUtils.sendModeratorAcceptedBudgetEmail(moderator, budgetInfo);

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    public void rejectBudget(final String budgetId, String comment) {
        budgetDAO.rejectBudget(budgetId);
        budgetDAO.updateBudgetComment(budgetId, comment);

        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    BudgetInfo budgetInfo = getBudget(budgetId);
                    List<Person> moderators = personManager.getModerators();

                    emailUtils.sendUserRejectedBudgetEmail(budgetInfo);

                    for (Person moderator : moderators)
                        emailUtils.sendModeratorRejectedBudgetEmail(moderator, budgetInfo);

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    @Override
    public void uploadTerms(String id, String contentType, InputStream inputStream) {
        budgetDAO.uploadTerms(id, contentType, inputStream);
    }

    @Override
    public BudgetFile downloadBudgetTerms(String budgetId) {
        return budgetDAO.downloadTerms(budgetId);
    }

    private BudgetInfo getBudgetInfo(Budget budget) {
        BudgetInfo budgetInfo = new BudgetInfo();

        budgetInfo.setId(budget.getId());
        budgetInfo.setDate(budget.getDate());
        budgetInfo.setStartDate(budget.getStartDate());
        budgetInfo.setEndDate(budget.getEndDate());
        budgetInfo.setAmountGranted(budget.getAmountGranted());
        budgetInfo.setAmountRequested(budget.getAmountRequested());
        budgetInfo.setRemaining(budget.getRemaining());
        budgetInfo.setCurrency(budget.getCurrency());
        budgetInfo.setStatusCode(budget.getStatusCode());
        budgetInfo.setStatus(getStatus(budget.getStatusCode()));
        budgetInfo.setComment(budgetDAO.getBudgetComment(budget.getId()));
        budgetInfo.setBankAccount(budget.getBankAccount());

        if (budget.getUser() != null) {
            try {
                budgetInfo.setUser(personManager.getById(budget.getUser()));
            } catch (PersonManagerException e) {
                e.printStackTrace();
            }
        }

        if (budget.getOrganisation() != null) {
            try {
                budgetInfo.setOrganisation(organizationManager.getById(budget.getOrganisation()));
            } catch (OrganizationManagerException e) {
                e.printStackTrace();
            }
        }

        return budgetInfo;
    }

    private Budget.Status getStatus(int statusCode) {
        for (Budget.Status s:Budget.Status.values())
            if ((statusCode & s.getCode()) == s.getCode())
                return s;

        return null;
    }
}
