/**
 *
 */
package org.msh.tb.cases;

import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Transactional;
import org.jboss.seam.annotations.security.Restrict;
import org.jboss.seam.core.Events;
import org.jboss.seam.international.Messages;
import org.jboss.seam.security.Identity;
import org.msh.tb.EntityHomeEx;
import org.msh.tb.MsgDispatcher;
import org.msh.tb.application.mail.MailService;
import org.msh.tb.entities.*;
import org.msh.tb.entities.enums.UserView;

import javax.persistence.EntityManager;
import java.util.Date;
import java.util.List;

/**
 * Services to handle case issues and its follow-up
 *
 * @author Ricardo Memoria
 *
 */
@Name("caseIssueHome")
public class CaseIssueHome extends EntityHomeEx<Issue> {
    private static final long serialVersionUID = 5972585923087719473L;

    public enum Action {
        NEWISSUE, READING_FOLLOWUPS, NEWFOLLOWUP, DEL_ISSUE, DEL_FOLLOWUP, CLOSE_ISSUE, REOPEN_ISSUE;
    }

    @In
    EntityManager entityManager;

    @In
    CaseHome caseHome;

    private List<Issue> list;
    private Integer count;
    private IssueFollowup followup;
    private Action action;
    private Integer followupId;


    /**
     * Create a new issue for the current case and logged user
     * @return String value "issue-created" if successfully created
     */
    @Restrict("#{s:hasRole('NEW_ISSUE')}")
    @Transactional
    public String createNewIssue() {
        Issue issue = getInstance();
        TbCase tbcase = caseHome.getInstance();

        issue.setCreationDate(new Date());
        issue.setTbcase(tbcase);
        issue.setUser(getUser());
        issue.setClosed(false);
        issue.setUser(getUser());
        issue.setUnit(getUserWorkspace().getTbunit());

        super.persist();
        caseHome.updateCaseTags();

        action = Action.NEWISSUE;
        Events.instance().raiseEvent(CaseEvent.NEW_ISSUE, issue);

        return "issue-created";
    }


    /**
     * Return the string to be displayed with the status of the issue
     * @param issue
     * @return String value
     */
    public String getDisplayStatus(Issue issue) {
        if (issue.isClosed()) {
            return Messages.instance().get("cases.closed");
        }

        if (issue.getAnswerDate() == null) {
            return Messages.instance().get("Issue.new");
        }

        return Messages.instance().get("Issue.answered");
    }


    /** {@inheritDoc}
     */
    @Override
    @Transactional
    public String remove() {
        if (!isManaged())
            throw new RuntimeException("Issue is not informed");

        // check permission
        // is not the owner of the user?
        if (isAllowedToCloseOrDelete(getInstance())) {
            throw new SecurityException("User is not authorized to delete this issue");
        }

        return super.remove();
    }

    /**
     * Post a new follow up for the given issue using the information
     * in the {@link CaseIssueHome#followup} variable
     * @return true if successfully saved
     */
    public boolean postFollowup() {
        if (followup == null)
            return false;

        if (!isAllowedToAnswer(getInstance()))
            throw new SecurityException("You don't have permission to post an answer");

        // prepare and save the follow up
        followup.setFollowupDate(new Date());
        followup.setIssue(getInstance());
        followup.setUser(getUser());
        followup.setUnit(getUserWorkspace().getTbunit());
        entityManager.persist(followup);

        // save the follow up date
        getInstance().setAnswerDate(followup.getFollowupDate());
        persist();
        caseHome.updateCaseTags();

        action = Action.NEWFOLLOWUP;
        return true;
    }


    /**
     * Notify a new issue by e-mail to the users in charge of responding it
     */
//    protected void notifyIssueByEmail() {
//        TbCase tbcase = caseHome.getInstance();
//        Issue issue = getInstance();
//        Tbunit unit = tbcase.getOwnerUnit();
//
//        List<User> lst = msgDispatcher.getUsersByRoleAndUnit("CASE_DATA", unit);
//
//        MailService srv = MailService.instance();
//
//
//        String code = unit.getAdminUnit().getCode();
//        String s = "";
//        String join = "";
//        while (code != null) {
//            s += join + "'" + code + "'";
//            code = AdministrativeUnit.getParentCode(code);
//            join = ",";
//        }
//
//        String hql = "select u.user from UserWorkspace u where u.profile.id in " +
//                "(select distinct p.id " +
//                "from UserProfile p join p.permissions perm " +
//                "where perm.userRole.name like :rolename " +
//                "and p.workspace.id = #{defaultWorkspace.id} " +
//                "and ((perm.userRole.changeable = true and perm.canChange = true) or (perm.userRole.changeable = false and perm.canExecute = true))" +
//                "and (u.view = :viewcountry or (u.view = :viewunit and u.tbunit.id = :unitid) " +
//                "or (u.view = :viewadm and u.tbunit.adminUnit.code in (" + s + "))))";
//
//        //Verificar a melhor forma de selecionar os usuarios
//        List<UserWorkspace> users = (List<UserWorkspace>) entityManager
//                .createQuery(hql)
//                .setParameter("viewcountry", UserView.COUNTRY)
//                .setParameter("viewunit", UserView.TBUNIT)
//                .setParameter("unitid", unit.getId())
//                .setParameter("viewadm", UserView.ADMINUNIT)
//                .setParameter("rolename", "CASE_DATA")
//                .getResultList();
//
//        for (UserWorkspace userW: users) {
//            srv.addComponent("user", userW.getUser());
//            srv.addComponent("issue", issue);
//            srv.addComponent("tbcase", tbcase);
//            srv.addComponent("isAnswer", issue.isClosed());
//            srv.addMessageToQueue("/mail/casetransfered.xhtml", userW.getUser().getTimeZone(), userW.getUser().getLanguage(), userW.getUser(), true);
//        }
//
//        srv.dispatchQueue();
//    }


    /**
     * Close the current issue
     */
    @Transactional
    public void closeIssue() {
        if ((!isManaged()) || (getInstance().isClosed()))
            throw new RuntimeException("Issue is not informed or it's already closed: " + getId());

        // check permission
        // is not the owner of the user?
        if (!isAllowedToCloseOrDelete(getInstance())) {
            throw new SecurityException("User is not authorized to close this issue");
        }

        getInstance().setClosed(true);
        persist();
        caseHome.updateCaseTags();

        action = Action.CLOSE_ISSUE;
    }


    /**
     * Check if user is allowed to close the given issue
     * @param issue instance of the {@link Issue} class
     * @return true if user can close the issue
     */
    public boolean isAllowedToCloseOrDelete(Issue issue) {
        if (issue == null)
            return false;
        // is not the owner of the user?
        if (getUser().getId().equals(issue.getUser().getId()))
            return true;
        return Identity.instance().hasRole("CLOSEDEL_ISSUE");
    }

    /**
     * Check if user is allowed to close the given issue
     * @param issue instance of the {@link Issue} class
     * @return true if user can close the issue
     */
    public boolean isAllowedToAnswer(Issue issue) {
        if (issue == null) {
            return false;
        }

        // is not the owner of the user?
        if (getUser().getId().equals(issue.getUser().getId())) {
            return true;
        }

        return Identity.instance().hasRole("ANSWER_ISSUE");
    }


    /**
     * Reopen a closed issue
     */
    @Transactional
    public void reopenIssue() {
        if ((!isManaged()) || (!getInstance().isClosed()))
            throw new RuntimeException("Issue is not informed or it's already open: " + getId());

        // check permission
        // is not the owner of the user?
        if (!isAllowedToCloseOrDelete(getInstance())) {
            throw new SecurityException("User is not authorized to open this issue");
        }

        getInstance().setClosed(false);
        persist();
        caseHome.updateCaseTags();

        action = Action.REOPEN_ISSUE;
    }

    /**
     * Return the number of open issues for a given case
     * @return integer value
     */
    public int getOpenIssuesCount() {
        if (count == null) {
            count = ((Long)entityManager
                    .createQuery("select count(*) from Issue where tbcase.id = #{caseHome.id}")
                    .getSingleResult()).intValue();
        }

        return count;
    }


    /**
     * Called to indicate that the follow-ups of the current issue will be rendered in the client side
     */
    public void loadFollowups() {
        action = Action.READING_FOLLOWUPS;
    }



    /**
     * Return the instance of the issue follow up being edited
     * @return instance of the {@link IssueFollowup} class
     */
    public IssueFollowup getFollowup() {
        if (followup == null)
            followup = new IssueFollowup();
        return followup;
    }

    /**
     * Return list of issues of a case
     * @return list of objects of class {@link Issue}
     */
    public List<Issue> getList() {
        if (list == null)
            createList();
        return list;
    }

    /**
     * Create the list of issues of a given case
     */
    protected void createList() {
        list = entityManager.createQuery("from Issue where tbcase.id = #{caseHome.id} order by creationDate")
                .getResultList();
    }


    /**
     * Delete the follow-up specified by the <code>followupId</code> property
     */
    public void deleteFollowup() {
        if (followupId == null)
            return;
        followup = entityManager.find(IssueFollowup.class, followupId);
        entityManager.remove(followup);
        caseHome.updateCaseTags();

        action = Action.DEL_FOLLOWUP;
    }


    /**
     * Delete the current issue
     */
    public void deleteIssue() {
        super.remove();

        action = Action.DEL_ISSUE;
    }

    /**
     * @return the action
     */
    public Action getAction() {
        return action;
    }


    /**
     * @return the followupId
     */
    public Integer getFollowupId() {
        return followupId;
    }


    /**
     * @param followupId the followupId to set
     */
    public void setFollowupId(Integer followupId) {
        this.followupId = followupId;
    }
}
