package gov.va.vinci.dart;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import javax.validation.Valid;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import gov.va.vinci.dart.biz.Comment;
import gov.va.vinci.dart.biz.DartRequest;
import gov.va.vinci.dart.biz.DocumentReviewNote;
import gov.va.vinci.dart.biz.DocumentReviewStatus;
import gov.va.vinci.dart.biz.Group;
import gov.va.vinci.dart.biz.OperationalRequest;
import gov.va.vinci.dart.biz.Person;
import gov.va.vinci.dart.biz.PreparatoryRequest;
import gov.va.vinci.dart.biz.Request;
import gov.va.vinci.dart.biz.RequestStatus;
import gov.va.vinci.dart.biz.RequestWorkflow;
import gov.va.vinci.dart.biz.Review;
import gov.va.vinci.dart.biz.ReviewTemplate;
import gov.va.vinci.dart.biz.Role;
import gov.va.vinci.dart.biz.WorkflowTemplate;
import gov.va.vinci.dart.common.exception.ObjectNotFoundException;
import gov.va.vinci.dart.common.json.ErrorView;
import gov.va.vinci.dart.db.util.HibernateSessionManager;
import gov.va.vinci.dart.dms.biz.Document;
import gov.va.vinci.dart.json.DenyReviewView;
import gov.va.vinci.dart.json.DocumentIdView;
import gov.va.vinci.dart.json.DocumentReviewNoteListView;
import gov.va.vinci.dart.json.DocumentReviewNoteView;
import gov.va.vinci.dart.json.DocumentReviewStatusListView;
import gov.va.vinci.dart.json.DocumentReviewStatusView;
import gov.va.vinci.dart.json.ErrorListView;
import gov.va.vinci.dart.json.RequestIdView;
import gov.va.vinci.dart.json.ReviewIdView;
import gov.va.vinci.dart.json.ReviewStatusView;
import gov.va.vinci.dart.json.ReviewTemplateListView;
import gov.va.vinci.dart.json.ReviewTemplateView;
import gov.va.vinci.dart.json.SaveDocumentReviewNoteView;
import gov.va.vinci.dart.json.SelectedReviewListView;
import gov.va.vinci.dart.json.atom.AtomLinkFactory;
import gov.va.vinci.dart.usr.UserPreferences;
import gov.va.vinci.dart.wf2.WorkflowResolver;

@Controller
public class ReviewController extends DartController {
    public static final SimpleDateFormat SDF = new SimpleDateFormat("MM/dd/yyyy");
    private static Log log = LogFactory.getLog(ReviewController.class);

    @Autowired
    private WorkflowResolver workflowResolver;

    @Autowired
    private AtomLinkFactory linkFactory;

    @Autowired
    private RequestController requestController;

    @RequestMapping(value = "/listAllReviewTemplates", method = RequestMethod.POST)
    @ResponseBody
    public Object listAllReviewTemplates(@RequestBody @Valid final RequestIdView requestView) { // WorkflowIdView

        log.debug("listAllReviewTemplates");

        try {
            HibernateSessionManager.start();

            ReviewTemplateListView result = new ReviewTemplateListView();

            if (requestView != null) {

                Request req = null;
                try {
                    req = RequestController.retrieveRequest(requestView.getRequestId());

                } catch (ObjectNotFoundException e) {
                    log.error("Error loading request " + requestView.getRequestId(), e);
                    return new ErrorView("Error loading request " + requestView.getRequestId());
                }

                List<ReviewTemplate> rtList = null;
                if (requestView.getWorkflowId() != 0
                        || (req != null && (req.getWorkflowTypeId() == WorkflowResolver.WF_RESEARCH_REQUEST
                                || req.getWorkflowTypeId() == WorkflowResolver.WF_PREPARATORY_REQUEST))) { // new request (after
                                                                                                           // the Independent
                                                                                                           // Workflow updates)
                    rtList = ReviewTemplate.listAllActive(); // get the active ReviewTemplates
                } else {
                    rtList = ReviewTemplate.listAll(); // get all of the ReviewTemplates, regardless of state
                } // end else

                if (rtList != null) {
                    for (ReviewTemplate rt : rtList) {
                        ReviewTemplateView view = new ReviewTemplateView();
                        view.setId(rt.getId());
                        view.setName(rt.getName());
                        view.setActive(rt.getActive());

                        result.getTemplates().add(view);
                    }
                } // end if

            } // end if -- requestView

            return result;
        } catch (Exception e) {
            log.error("Error retrieving all review templates.", e);
            HibernateSessionManager.rollback();

            return new ErrorView("Error retrieving all review templates.");
        } finally {
            HibernateSessionManager.close();
        }
    }

    @RequestMapping(value = "/listSelectedReviewTemplates", method = RequestMethod.POST)
    @ResponseBody
    public Object listSelectedReviewTemplates(@RequestBody @Valid final RequestIdView view) {

        log.debug("listSelectedReviewTemplates");

        try {
            HibernateSessionManager.start();

            UserPreferences prefs = getUserPreferences();

            Person person = null;
            try {
                person = Person.findById(prefs.getUserId());
            } catch (ObjectNotFoundException e) {
                throw new ObjectNotFoundException("Cannot find person with id: " + prefs.getUserId());
            }
            Role.initialize();

            if (person.hasRole(Role.NDS_ADMIN) == false && person.hasRole(Role.SUPER_USER) == false) {
                return new ErrorView("User does not have permission to perform this action.");
            }

            ReviewTemplateListView result = new ReviewTemplateListView();

            Request req = null;
            try {
                req = RequestController.retrieveRequest(view.getRequestId());

            } catch (ObjectNotFoundException e) {
                log.error("Error loading request " + view.getRequestId(), e);
                return new ErrorView("Error loading request " + view.getRequestId());
            }

            if (req != null) {

                Set<Review> reviewSet = null;
                RequestWorkflow workflow = null;
                if (view.getWorkflowId() != 0) { // new request (after the Independent Workflow updates)

                    workflow = RequestWorkflow.findById(view.getWorkflowId());
                    if (workflow != null && workflow.getWorkflowTemplate().getWorkflowTypeId() == WorkflowResolver.WF_NDS) {
                        reviewSet = req.getReviews(workflow);
                    } // end if

                } else { // old request or the super-user

                    if (req.getWorkflowTypeId() == WorkflowResolver.WF_NDS) { // old NDS request

                        reviewSet = req.getAllReviews(); // use all of the reviews attached to this request

                    } else if (req.getWorkflowTypeId() == WorkflowResolver.WF_RESEARCH_REQUEST
                            || req.getWorkflowTypeId() == WorkflowResolver.WF_PREPARATORY_REQUEST) { // new request

                        Group.initialize();
                        WorkflowTemplate workflowTemplate =
                                WorkflowTemplate.findByGroupIdAndWorkflowTypeId(Group.NDS.getId(), WorkflowResolver.WF_NDS);
                        if (workflowTemplate != null) {
                            workflow = RequestWorkflow.findOpenByRequestAndWorkflowTemplateId(req.getId(),
                                    workflowTemplate.getId()); // get the open NDS workflow if there is one. Otherwise, there
                                                               // are no review templates to retrieve.
                            if (workflow != null) {
                                reviewSet = req.getReviews(workflow);
                            } // end if
                        } // end if

                    }
                } // end else

                // get the intermediate reviews for this workflow (regardless of the state of the associated ReviewTemplate)
                if (reviewSet != null) {
                    for (Review review : reviewSet) {
                        if (!review.isWithdrawn()) {
                            
                            ReviewTemplate template = ReviewTemplate.findByGroupId(review.getReviewer().getId());
                            if (template != null) {
                                ReviewTemplateView templateView = new ReviewTemplateView();
                                templateView.setId(template.getId());
                                templateView.setName(template.getName());
                                templateView.setActive(template.getActive());

                                result.getTemplates().add(templateView);
                            }
                        }
                    }
                }
            } 

            return result;
        } catch (Exception e) {
            log.error("Error retrieving the selected review templates.", e);
            HibernateSessionManager.rollback();

            return new ErrorView("Error retrieving the selected review templates.");
        } finally {
            HibernateSessionManager.close();
        }
    }

    /**
     * Initial NDS review and selection of the intermediate reviews for the NDS workflow.
     * 
     * @param view
     * @return
     */
    @RequestMapping(value = "/selectReviews", method = RequestMethod.POST)
    @ResponseBody
    public Object selectReviews(@RequestBody @Valid final SelectedReviewListView view) {

        log.debug("selectReviews");

        UserPreferences prefs = null;

        boolean chainApproveRequest = false;
        Request request = null;

        try {
            HibernateSessionManager.start();

            prefs = getUserPreferences();

            // first - is user in Irma role?
            Role.initialize();
            Person person = Person.findById(prefs.getUserId());

            if (person.hasRole(Role.NDS_ADMIN) == false) {
                return new ErrorView("User does not have the necessary permission to perform this review.");
            }

            try {
                request = RequestController.retrieveRequest(view.getRequestId());

            } catch (ObjectNotFoundException e) {
                log.error("Error loading request " + view.getRequestId(), e);
                return new ErrorView("Error loading request " + view.getRequestId());
            }

            if (view.getReviewTemplates() != null && view.getReviewTemplates().size() < 1) {
                // no intermediate reviews selected.
                chainApproveRequest = true;
            }

            // get the workflow for this NDS review
            RequestWorkflow workflow = null;
            if (view.getWorkflowId() != 0) { // new request (after the Independent Workflow updates)
                workflow = RequestWorkflow.findById(view.getWorkflowId());
            }

            if (request != null) {

                List<Group> reviewGroupList = new ArrayList<Group>();

                for (String id : view.getReviewTemplates()) {
                    // Integer from String
                    Integer intId = Integer.parseInt(id);
                    ReviewTemplate template = ReviewTemplate.findById(intId);

                    if (template != null) { // verify that the template exists

                        // only add this review if prior to the independent workflows or this template is currently active
                        // (supported for the new request)
                        if (workflow == null || template.getActive() == true) {

                            reviewGroupList.add(template.getReviewer()); // selected this intermediate review group

                            // only create a new review if this request doesn't already have a review for this group
                            Review reviewForGroup = request.findReviewAssignedToGroup(workflow, template.getReviewer());
                            if (reviewForGroup == null) {

                                Review.create(workflow, request, template, template.getReviewer(), prefs.getUserLoginId());

                            }
                        }
                    }
                }

                // remove any extra reviews from this request (no longer selected this intermediate review group)
                List<Review> reviewsToBeRemoved = new ArrayList<Review>();

                for (Review rev : request.getReviews(workflow)) {
                    if (reviewGroupList.contains(rev.getReviewer()) == false) { // this group is no longer in the list of
                                                                                // selected review groups
                        reviewsToBeRemoved.add(rev); // mark the review to be removed
                    } else { // selected review groups

                        if (workflow != null) { // new request, after the Independent Workflow updates

                            // if the selected review group is no longer active, remove it (no longer supported as an
                            // intermediate review group)
                            Group revGroup = rev.getReviewer();
                            if (revGroup != null) {
                                ReviewTemplate template = ReviewTemplate.findByGroupId(revGroup.getId());
                                if (template == null || template.getActive() == false) { // this group is no longer supported as
                                                                                         // an intermediate review group
                                    reviewsToBeRemoved.add(rev); // mark the review to be removed
                                }
                            }
                        }
                    }
                }
                request.removeReviewList(reviewsToBeRemoved, true); // remove these reviews (no longer selected)

            }
          //resolve top-level workflow, take action at the specific workflow
            workflowResolver.resolve(request).approve(workflow, null, request, prefs.getUserLoginId()); 
        } catch (Exception e) {
            log.error("Error selecting request reviews and performing initial NDS approval.", e);
            HibernateSessionManager.rollback();
            return new ErrorView("Error selecting request reviews and performing initial NDS approval.");
        } finally {
            HibernateSessionManager.close();
        }

        // Irma now wants the OperationalRequest to only have one initial NDS review (4/23)
        // for Operations workflow, do NOT close the chained review (has no intermediate reviews)
        if (request != null && !(OperationalRequest.class.isAssignableFrom(request.getClass()))) {

            // TODO- perhaps we allow the workflow to handle this

            // outside of the Hibernate transaction
            if (chainApproveRequest == true) {
                // no reviews selected.
                RequestIdView riv = new RequestIdView();
                riv.setRequestId(view.getRequestId());
                riv.setWorkflowId(view.getWorkflowId());
                riv.setUserId(prefs.getUserId());
                return requestController.approveRequest(riv);
            }
        }

        return new ErrorView("OK");
    }

    /**
     * Non-NDS review (Intermediate review): approve
     * 
     * @param view
     * @return
     */
    @RequestMapping(value = "/approveReview", method = RequestMethod.POST)
    @ResponseBody
    public Object approveReview(@RequestBody @Valid final ReviewIdView view) {
        log.debug("approveReview");

        try {
            HibernateSessionManager.start();

            UserPreferences prefs = getUserPreferences();

            Review review = Review.findById(view.getReviewId());
            if (review == null) {
                return new ErrorView("Error:  Failed to load review.");
            }

            if (review.isApproved() || review.isRejected() || review.isWithdrawn()) {
                return new ErrorView("Review has already been approved, denied or withdrawn.");
            }

            Person approver = Person.findById(prefs.getUserId());

            Role.initialize();

            if (approver.hasRole(Role.REVIEWER) == false) {
                return new ErrorView("User does not have permission to review requests.");
            }
            if (approver.inGroup(review.getReviewer()) == false) {
                return new ErrorView("User does not belong to appropriate group to perform this review: "
                        + review.getReviewer().getShortName());
            }

            // get the workflow (support both old and new request data)
            RequestWorkflow workflow = null;
            if (view.getWorkflowId() != 0) { // new request (after the Independent Workflow updates)
                workflow = RequestWorkflow.findById(view.getWorkflowId());
            }

            workflowResolver.resolve(review.getRequest()).approve(workflow, review, review.getRequest(),
                    prefs.getUserLoginId()); // resolve top-level workflow, take action at the specific workflow

            return new ErrorView("OK");

        } catch (Exception e) {
            log.error("Error approving review.", e);
            HibernateSessionManager.rollback();
            return new ErrorView("Error approving review");
        } finally {
            HibernateSessionManager.close();
        }
    }

    /**
     * Non-NDS review (Intermediate review): deny
     * 
     * @param view
     * @return
     */
    @RequestMapping(value = "/rejectReview", method = RequestMethod.POST)
    @ResponseBody
    public Object rejectReview(@RequestBody @Valid final DenyReviewView view) {

        log.debug("rejectReview");

        try {
            HibernateSessionManager.start();

            UserPreferences prefs = getUserPreferences();

            Review review = Review.findById(view.getReviewId());
            if (review == null) {
                return new ErrorView("Error:  Failed to load review.");
            }

            Person approver = Person.findById(prefs.getUserId());

            Role.initialize();
            Group.initialize();

            if (approver.hasRole(Role.REVIEWER) == false) {
                return new ErrorView("User does not have permission to review requests.");
            }
            if (approver.inGroup(review.getReviewer()) == false) {
                return new ErrorView("User does not belong to appropriate group to perform this review: "
                        + review.getReviewer().getShortName());
            }

            // save a communication
            Comment.create("Review Denied", review.getRequest(), prefs.getUserLoginId(),
                    "VINCI Dart request " + review.getRequest().getTrackingNumber() + " is denied by "
                            + review.getReviewer().getShortName() + ". Reason supplied by reviewer: " + view.getText());

            // get the workflow (support both old and new request data)
            RequestWorkflow workflow = null;
            if (view.getWorkflowId() != 0) { // new request (after the Independent Workflow updates)
                workflow = RequestWorkflow.findById(view.getWorkflowId());
            }

            // deny this review (non-NDS)
          //resolve top-level workflow, take action at the specific workflow
            workflowResolver.resolve(review.getRequest()).deny(workflow, review, review.getRequest(), prefs.getUserLoginId()); 
            return new ErrorView("OK");
        } catch (Exception e) {
            log.error("Error denying review", e);
            HibernateSessionManager.rollback();
            return new ErrorView("Error denying review");
        } finally {
            HibernateSessionManager.close();
        }
    }

    /**
     * Validate the initial NDS review
     * 
     * @param view
     * @return
     */
    @RequestMapping(value = "/validateInitialNDSReview", method = RequestMethod.POST)
    @ResponseBody
    public Object validateInitialNDSReview(@RequestBody @Valid final RequestIdView view) {

        log.debug("validateInitialNDSReview");

        List<String> msgs = new ArrayList<String>();
        ErrorListView result = new ErrorListView("Error");

        try {
            HibernateSessionManager.start();

            UserPreferences prefs = getUserPreferences();

            Request request = RequestController.retrieveRequest(view.getRequestId());

            Person approver = Person.findById(prefs.getUserId());
            Role.initialize();
            Group.initialize();

            RequestWorkflow ndsWorkflow = null;
            if (view.getWorkflowId() != 0) { // new request (after the Independent Workflow updates)
                ndsWorkflow = RequestWorkflow.findById(view.getWorkflowId());
            }

            // verify the request state for this NDS review
            if (request.isSubmittedOrChanged(null) == false) { // request status

                if (approver.hasRole(Role.SUPER_USER) == false) { // not the super-user

                    RequestStatus requestStatus = request.getStatus(); // use the request-level status

                    String requestStatusName = requestStatus.getName();
                    if (RequestStatus.INITIATED.getId() == requestStatus.getId())
                        requestStatusName = "Not Submitted";

                    msgs.add("Error:  Request is in the wrong state for review (" + requestStatusName
                            + ").  Cannot submit this review.");
                } // end if -- super-user

            } else { // request in an okay state

                // verify the workflow state for this NDS review
                if (ndsWorkflow != null) {
                    if (request.isSubmittedOrChanged(ndsWorkflow) == false) { // workflow status

                        if (approver.hasRole(Role.SUPER_USER) == false) { // not the super-user

                          //use the workflow-specific status (want to know the status of this specific workflow)
                            RequestStatus workflowStatus = ndsWorkflow.getRequestStatus(); 

                            String workflowStatusName = workflowStatus.getName();
                            if (RequestStatus.INITIATED.getId() == workflowStatus.getId())
                                workflowStatusName = "Not Submitted";

                            msgs.add("Error:  Request is in the wrong state for review (" + workflowStatusName
                                    + ").  Cannot submit this review.");
                        }
                    }
                }
            }

            // let the super-user see the state of the request (can also just click on the status visualization piece)
            if (approver.hasRole(Role.SUPER_USER)) { // super-user role (allow the super-user to see the review validation, but
                                                     // not to actually perform a review)

                if (RequestStatus.INITIATED.getId() == request.getStatus().getId())
                    result.setInitiated(true);
                if (RequestStatus.SUBMITTED.getId() == request.getStatus().getId())
                    result.setSubmitted(true);

                // get the list of review status info for all workflows attached to this request
                result.getStatus().addAll(getReviewStatusListForSuperUser(request));

            } else {

                if (approver.hasRole(Role.NDS_ADMIN) == false) {
                    msgs.add("Error:  User does not have permission to review this request.");
                }
                if (approver.inGroup(Group.NDS) == false) {
                    msgs.add("User does not belong to appropriate group to perform this review: " + Group.NDS.getShortName());
                }

                // only submit the review once

                if (!workflowResolver.resolve(request).isReadyForInitialReview(ndsWorkflow, request)) {
                    // what is the status of the initial NDS review?
                    String status = request.getNDSReviewStatus(ndsWorkflow, Group.NDS, true);
                    // Allow additional reviewers to be selected/deselected on the decision page
                    if (status != null && !status.equals(RequestStatus.APPROVED.getName())) {
                        msgs.add("Error:  Initial NDS review has already been submitted (" + status + ").");
                    }
                }
            }

            //
            // all these error messages should be collected into a single list to return
            result.getErrors().addAll(msgs);

            if (result.getErrors().size() < 1) {
                result.setMsg("OK");
            }

        } catch (Exception e) {
            log.error("Error validating review.", e);
            HibernateSessionManager.rollback();

            // return new ErrorView("Error validating review");
            result.getErrors().add("Error validating review");
            return result;
        } finally {
            HibernateSessionManager.close();
        }

        return result;
    }

    /**
     * Get the per-workflow status details for the super-user UI display.
     * 
     * @param workflow
     * @param req
     * @return
     */
    @SuppressWarnings("unchecked")
    public static List<String> getWorkflowReviewStatusListForSuperUser(final RequestWorkflow workflow, final Request req) {

        List<String> reviewStatusList = new ArrayList<String>();
        List<ReviewStatusView> statusViewList = new ArrayList<ReviewStatusView>();

        boolean getNDSWorkflow = false; // retrieve the NDS workflow status?
        if (workflow == null || (workflow.getWorkflowTemplate() != null
                && workflow.getWorkflowTemplate().getWorkflowTypeId() == WorkflowResolver.WF_NDS)) {
            getNDSWorkflow = true;
        }

        if (getNDSWorkflow == true) { // NDS workflow

            // status of the initial NDS review
            String initNDSStatus = req.getNDSReviewStatus(workflow, Group.NDS, true);
            String groupShortName = Review.INITIAL_NDS_GROUP + " Review";

            ReviewStatusView initNDSReview = new ReviewStatusView();
            initNDSReview.setGroupName(groupShortName);
            initNDSReview.setStatus(initNDSStatus);
            initNDSReview.setSortOrder(ReviewStatusView.getSortOrderForGroup(Group.NDS, true)); // initial NDS review
            statusViewList.add(initNDSReview);

            // Until the initial NDS review has been completed, no other reviews should be shown. including the total
            if (initNDSStatus != null && initNDSStatus.equals(RequestStatus.APPROVED.getName())) {

                // status of the intermediate reviews (none for OperationalRequest)
                Set<Review> reviewSet = req.getReviews(workflow); // get the reviews for this workflow
                if (reviewSet != null) {
                    for (Review review : reviewSet) {
                        String reviewStatus = review.getReviewStatus();
                        groupShortName = review.getReviewer().getShortName(); // + " Review";

                        ReviewStatusView currStatusView = new ReviewStatusView();
                        currStatusView.setGroupName(groupShortName);
                        currStatusView.setStatus(reviewStatus);
                        currStatusView.setSortOrder(ReviewStatusView.getSortOrderForGroup(review.getReviewer(), false));
                        statusViewList.add(currStatusView);
                    }
                } // end if -- reviewSet

                // status of the final NDS review
                // The Final NDS approval should not indicate waiting for review until it actually goes into their To Do List.
                // Once all the other approvals are granted, then it would indicate waiting for review.
                if (req.allReviewsApproved(workflow)) {

                    String finalNDSStatus = req.getNDSReviewStatus(workflow, Group.NDS, false);
                    groupShortName = Review.FINAL_NDS_GROUP + " Review";

                    ReviewStatusView finalNDSReview = new ReviewStatusView();
                    finalNDSReview.setGroupName(groupShortName);
                    finalNDSReview.setStatus(finalNDSStatus);
                    finalNDSReview.setSortOrder(ReviewStatusView.getSortOrderForGroup(Group.NDS, false)); // final NDS review
                    statusViewList.add(finalNDSReview);
                } // end if

            } // end if -- initial NDS review done

        } else { // non-NDS workflow

            // retrieve the status of the reviews attached to this workflow
            Set<Review> reviewSet = req.getReviews(workflow); // get the reviews for this workflow
            if (reviewSet != null) {
                for (Review review : reviewSet) {
                    String reviewStatus = review.getReviewStatus();
                    String groupShortName = review.getReviewer().getShortName(); // + " Review";

                    ReviewStatusView currStatusView = new ReviewStatusView();
                    currStatusView.setGroupName(groupShortName);
                    currStatusView.setStatus(reviewStatus);
                    currStatusView.setSortOrder(ReviewStatusView.getSortOrderForGroup(review.getReviewer(), false));
                    statusViewList.add(currStatusView);
                }
            } // end if -- reviewSet

        } // end else

        // get the sorted list of review group and status string
        Collections.sort(statusViewList);
        for (ReviewStatusView view : statusViewList) {
            reviewStatusList.add(view.getStatusDisplayString());
        }

        return reviewStatusList;
    }

    /**
     * Get the NDS workflow status details for the DART Admin UI display.
     * 
     * @param workflow
     * @param req
     * @return
     */
    public static List<String> getReviewStatusListForSuperUser(final Request req) {

        List<String> allStatusList = new ArrayList<String>();

        // step through all workflows attached to this request, and retrieve the status details for each review
        Set<RequestWorkflow> workflowSet = req.getWorkflows(true);
        if (workflowSet != null && workflowSet.isEmpty() == false) { // have some workflows attached to this request

            for (RequestWorkflow workflow : workflowSet) {

                // get the status details for the current workflow
                List<String> workflowStatusList = getWorkflowReviewStatusListForSuperUser(workflow, req);
                if (workflowStatusList != null && workflowStatusList.isEmpty() == false) {

                    if (allStatusList.isEmpty() == false)
                        allStatusList.add("");

                    allStatusList.addAll(workflowStatusList);
                }
            } // end for

        } else { // before the separation of workflows, so just get the overall status

            // get the status details for the entire request
            return (getWorkflowReviewStatusListForSuperUser(null, req));

        } // end else

        return allStatusList;
    }

    /**
     * Validate the final NDS review
     * 
     * @param request
     * @return
     */
    @RequestMapping(value = "/validateFinalNDSReview", method = RequestMethod.POST)
    @ResponseBody
    public Object validateFinalNDSReview(@RequestBody @Valid final RequestIdView view) {

        log.debug("validateFinalNDSReview");

        List<String> msgs = new ArrayList<String>();
        ErrorListView result = new ErrorListView("Error");

        try {
            HibernateSessionManager.start();

            UserPreferences prefs = getUserPreferences();

            Person approver = Person.findById(prefs.getUserId());
            Role.initialize();
            Group.initialize();

            // if old data, use all reviews attached to the request. Otherwise, use only the NDS reviews attached to this
            // request.
            RequestWorkflow workflow = null;
            if (view.getWorkflowId() != 0) { // new request (after the Independent Workflow updates)
                workflow = RequestWorkflow.findById(view.getWorkflowId());
            }

            Request req = RequestController.retrieveRequest(view.getRequestId());

            // is the request in a reviewable state?
            if (req.isSubmittedOrChanged(null) == false) { // request status

                if (approver.hasRole(Role.SUPER_USER) == false) { // NOT the super-user role, so get the request status

                    RequestStatus requestStatus = req.getStatus(); // use the request-level status

                    String requestStatusName = requestStatus.getName();
                    if (RequestStatus.INITIATED.getId() == requestStatus.getId())
                        requestStatusName = "Not Submitted";

                    msgs.add("Error:  Request is in the wrong state for review (" + requestStatusName
                            + ").  Cannot submit this review.");
                } // end if

            } else { // request in an okay state

                // is the workflow in a reviewable state?
                if (workflow != null) {
                    if (req.isSubmittedOrChanged(workflow) == false) { // workflow status

                        if (approver.hasRole(Role.SUPER_USER) == false) { // NOT the super-user role, so get the workflow status

                            RequestStatus workflowStatus = workflow.getRequestStatus(); // use the workflow-specific status

                            String workflowStatusName = workflowStatus.getName();
                            if (RequestStatus.INITIATED.getId() == workflowStatus.getId())
                                workflowStatusName = "Not Submitted";

                            msgs.add("Error:  Request is in the wrong state for review (" + workflowStatusName
                                    + ").  Cannot submit this review.");
                        } // end if
                    } // end if
                } // end if -- workflow

            } // end else

            // let the super-user see the state of the request (can also just click on the status visualization piece)
            if (approver.hasRole(Role.SUPER_USER)) { // super-user role

                if (RequestStatus.INITIATED.getId() == req.getStatus().getId())
                    result.setInitiated(true);
                if (RequestStatus.SUBMITTED.getId() == req.getStatus().getId())
                    result.setSubmitted(true);

                // get the list of review status info
                result.getStatus().addAll(getReviewStatusListForSuperUser(req));

            } else { // NOT a super-user

                if (approver.hasRole(Role.NDS_ADMIN) == false) { // wrong group/role
                    msgs.add("Error:  User does not have permission to review this request.");
                }
                if (approver.inGroup(Group.NDS) == false) {
                    msgs.add("User does not belong to appropriate group to perform this review: " + Group.NDS.getShortName());
                }

                if (req.isSubmittedOrChanged(workflow) == true) { // check the request state

                    //
                    // has the final NDS review already been submitted?
                    String status = req.getNDSReviewStatus(workflow, Group.NDS, false); // what is the status of the final NDS
                                                                                        // review?
                    if (status.equals(Review.WAITING_FOR_REVIEW) == false && status.isEmpty() == false) {

                        msgs.add("Error:  Final NDS review has already been submitted (" + status + ").");
                    } // end if

                } // end if

                // for final NDS review, verify that all intermediate reviews have been processed
                if (DartRequest.class.isAssignableFrom(req.getClass())
                        || PreparatoryRequest.class.isAssignableFrom(req.getClass())) {

                    if ((workflow == null || workflow.getWorkflowTemplate().getWorkflowTypeId() == WorkflowResolver.WF_NDS)
                            && !req.allReviewsCompleted(workflow)) {
                        msgs.add("Error: Cannot submit the final review until all intermediate reviews have been completed.");
                    }

                } // end if -- DartRequest

            } // end else -- not a super-user

            //
            // all these error messages should be collected into a single list to return
            result.getErrors().addAll(msgs);

            if (result.getErrors().size() < 1) {
                result.setMsg("OK");
            }

        } catch (Exception e) {
            log.error("Error validating review.", e);
            HibernateSessionManager.rollback();

            // return new ErrorView("Error validating review");
            result.getErrors().add("Error validating review");
            return result;
        } finally {
            HibernateSessionManager.close();
        }

        return result;
    }

    /**
     * Validate the intermediate review (review group other than NDS)
     * 
     * @param view
     * @return
     */
    @RequestMapping(value = "/validateReview", method = RequestMethod.POST)
    @ResponseBody
    public Object validateReview(@RequestBody @Valid final ReviewIdView view) {
        log.debug("validateReview");

        List<String> msgs = new ArrayList<String>();
        ErrorListView result = new ErrorListView("Error");

        try {
            HibernateSessionManager.start();

            UserPreferences prefs = getUserPreferences();

            Person approver = Person.findById(prefs.getUserId());

            Role.initialize();
            Group.initialize();

            RequestWorkflow workflow = null;
            if (view.getWorkflowId() != 0) { // new request (after the Independent Workflow updates)
                workflow = RequestWorkflow.findById(view.getWorkflowId());
            }

            Review review = Review.findById(view.getReviewId());
            if (review == null) {
                msgs.add("Error:  There is no review to be performed for this request.");

            } else {

                // let the super-user see the state of the request (can also just click on the status visualization piece)
                if (approver.hasRole(Role.SUPER_USER)) { // super-user role

                    if (RequestStatus.INITIATED.getId() == review.getRequest().getStatus().getId())
                        result.setInitiated(true);
                    if (RequestStatus.SUBMITTED.getId() == review.getRequest().getStatus().getId())
                        result.setSubmitted(true);

                    // get the list of review status info
                    result.getStatus().addAll(getReviewStatusListForSuperUser(review.getRequest()));

                } else if (approver.hasRole(Role.READ_ONLY_STAFF)) { // read-only staff role

                    // retrieve the status of this review (for display only)
                    ReviewStatusView currStatusView = new ReviewStatusView();
                    currStatusView.setGroupName(review.getReviewer().getShortName());
                    currStatusView.setStatus(review.getReviewStatus());

                    // review status info
                    result.getStatus().add(currStatusView.getStatusDisplayString());

                } else { // NOT a super-user or read-only staff

                    //
                    // verify the request status (a closed request cannot be approved/rejected)
                    if (review.getRequest().isSubmittedOrChanged(null) == false) { // request status
                      //NOT a super-user user, so get the status that is specific to this review group
                        if (approver.hasRole(Role.SUPER_USER) == false && approver.hasRole(Role.READ_ONLY_STAFF) == false) {

                            RequestStatus requestStatus = review.getRequest().getStatus(); // use the request-level status

                            String requestStatusName = requestStatus.getName();
                            if (RequestStatus.INITIATED.getId() == requestStatus.getId())
                                requestStatusName = "Not Submitted";

                            msgs.add("Error:  Request is in the wrong state for review (" + requestStatusName
                                    + ").  Cannot submit this review.");
                        } // end if -- NOT the super-user

                    } else { // request in an okay state

                        //
                        // verify the workflow status, if it exists
                        if (workflow != null) {
                            if (review.getRequest().isSubmittedOrChanged(workflow) == false) { // workflow status
                              //NOT a super-user user, so get the status that is specific to this review group
                                if (approver.hasRole(Role.SUPER_USER) == false
                                        && approver.hasRole(Role.READ_ONLY_STAFF) == false) {

                                    RequestStatus workflowStatus = workflow.getRequestStatus(); // use the workflow-specific
                                                                                                // status

                                    String workflowStatusName = workflowStatus.getName();
                                    if (RequestStatus.INITIATED.getId() == workflowStatus.getId())
                                        workflowStatusName = "Not Submitted";

                                    msgs.add("Error:  Request is in the wrong state for review (" + workflowStatusName
                                            + ").  Cannot submit this review.");
                                } // end if -- NOT the super-user

                            } // end if -- workflow status
                        } // end else -- workflow state

                        //
                        // cannot submit an intermediate review until the initial NDS review has been approved (NDS workflow)
                        if ((workflow == null
                                || workflow.getWorkflowTemplate().getWorkflowTypeId() == WorkflowResolver.WF_NDS)) { // NDS
                                                                                                                     // workflow
                          //what is the status of the initial NDS review?
                            String initialNDSStatus = review.getRequest().getNDSReviewStatus(workflow, Group.NDS, true); 

                            // boolean bInitReviewSubmitted = false;
                            // if( workflow == null ) {
                            // bInitReviewSubmitted =
                            // workflowResolver.resolve(review.getRequest()).isInitialReviewCompleted(workflow,
                            // review.getRequest()); //has the initial NDS review been submitted?
                            // } else {
                            // bInitReviewSubmitted = workflowResolver.resolve(workflow).isInitialReviewCompleted(workflow,
                            // review.getRequest()); //has the initial NDS review been submitted?
                            // }
                            //
                            // if( initialNDSStatus.equals(RequestStatus.APPROVED.getName()) == false || bInitReviewSubmitted ==
                            // false ) { //initial NDS review not yet approved
                            if (initialNDSStatus.equals(RequestStatus.APPROVED.getName()) == false) { // initial NDS review not
                                                                                                      // yet approved
                                msgs.add("Error:  Cannot submit this review until the Initial NDS review has been approved.");
                            }
                        } // end if -- NDS workflow

                    } // end else -- request state

                    //
                    // only submit the review once (approved, denied, or change request)
                    if (review.isApproved() || review.isRejected() || review.isChangeRequested() || review.isWithdrawn()) {

                        String stateErrMsg = "Error:  Review has already been submitted";

                        if (review.isApproved())
                            stateErrMsg += " (" + RequestStatus.APPROVED.getName() + ")";
                        else if (review.isRejected())
                            stateErrMsg += " (" + RequestStatus.DENIED.getName() + ")";
                        else if (review.isChangeRequested())
                            stateErrMsg += " (" + RequestStatus.CHANGE_REQUESTED.getName() + ")";
                        else if (review.isWithdrawn())
                            stateErrMsg += " (" + Review.WITHDRAWN + ")";
                        msgs.add(stateErrMsg + ".");
                    }

                    if (approver.hasRole(Role.REVIEWER) == false && approver.hasRole(Role.READ_ONLY_STAFF) == false) {
                        msgs.add("Error:  User does not have permission to review requests.");
                    }
                    if (approver.inGroup(review.getReviewer()) == false) {
                        msgs.add("Error:  User does not belong to appropriate group to perform this review: "
                                + review.getReviewer().getShortName());
                    }
                }
            }

            // all these error messages should be collected into a single list to return
            result.getErrors().addAll(msgs);

            if (result.getErrors().size() < 1) {
                result.setMsg("OK");
            }

        } catch (Exception e) {
            log.error("Error validating review.", e);
            HibernateSessionManager.rollback();

            // return new ErrorView("Error validating review");
            result.getErrors().add("Error validating review");
            return result;
        } finally {
            HibernateSessionManager.close();
        }

        return result;
    }

    @RequestMapping(value = "/setDocumentReviewStatus", method = RequestMethod.POST)
    @ResponseBody
    public Object setDocumentReviewStatus(@RequestBody @Valid final DocumentReviewStatusView request) {

        log.debug("setDocumentReviewStatus");

        try {
            HibernateSessionManager.start();

            UserPreferences prefs = getUserPreferences();

            Document doc = Document.findById(request.getDocId());
            if (doc == null) {
                log.error("Document id " + request.getDocId() + " does not exist.  Cannot save the document review status.");
                return new ErrorView("Error saving the document review status for documentId: " + request.getDocId());
            }

            DocumentReviewStatus drs = null;

            // get the user's primary group
            Person pers = Person.findById(prefs.getUserId());
            Group grp = pers.getGroups().iterator().next();

            if (grp == null) {
                return new ErrorView("Error: User does not have permission to perform this action.");
            }

            Role.initialize();
            if (pers.hasRole(Role.NDS_ADMIN) == false && pers.hasRole(Role.REVIEWER) == false
                    && pers.hasRole(Role.READ_ONLY_STAFF) == false && pers.hasRole(Role.SUPER_USER) == false) {
                return new ErrorView("Error: User does not have permission to perform this action.");
            }

            // if( pers.hasRole(Role.SUPER_USER) == false ) { //super-user role is read-only (shouldn't need to set the document
            // review status)
            try {
                // drs = DocumentReviewStatus.findById(doc.getId(), pers.getId(), grp.getId());
                drs = DocumentReviewStatus.findByGroupId(doc.getId(), grp.getId());
            } catch (ObjectNotFoundException e) {
            } // ok - just ignore it then

            if (drs == null) {
                drs = DocumentReviewStatus.create(doc, pers, grp, request.getStatus());
            }

            drs.setStatus(request.getStatus());
            // }//end if -- superuser

            return new ErrorView("OK");
        } catch (Exception e) {
            log.error("Error setting document review status", e);
            HibernateSessionManager.rollback();

            return new ErrorView("Error setting document review status");
        } finally {
            HibernateSessionManager.close();
        }
    }

    @RequestMapping(value = "/listDocumentReviewStatus", method = RequestMethod.POST)
    @ResponseBody
    public Object listDocumentReviewStatus(@RequestBody @Valid final RequestIdView request) {

        log.debug("listDocumentReviewStatus");

        DocumentReviewStatusListView result = new DocumentReviewStatusListView();

        try {
            HibernateSessionManager.start();

            UserPreferences prefs = getUserPreferences();

            // get the user's primary group
            Person pers = Person.findById(prefs.getUserId());
            Group grp = pers.getGroups().iterator().next();

            if (grp == null) {
                return new ErrorView("Error: User does not have permission to perform this action.");
            }

            Role.initialize();
            if (pers.hasRole(Role.NDS_ADMIN) == false && pers.hasRole(Role.REVIEWER) == false
                    && pers.hasRole(Role.READ_ONLY_STAFF) == false && pers.hasRole(Role.SUPER_USER) == false) {
                return new ErrorView("Error: User does not have permission to perform this action.");
            }

            List<DocumentReviewStatus> drsList = DocumentReviewStatus.listByReviewerId(grp.getId());

            for (DocumentReviewStatus drs : drsList) {
                Document doc = drs.getDocument();
                if (doc.getRequest().getId() == request.getRequestId()) {
                    // add to result list
                    DocumentReviewStatusView view = new DocumentReviewStatusView();
                    view.setDocId(doc.getId());
                    view.setStatus(drs.getStatus());

                    result.getStatuses().add(view);
                }
            }

            return result;
        } catch (Exception e) {
            log.error("Error retrieving document review status.", e);
            HibernateSessionManager.rollback();
            return new ErrorView("Error retrieving document review status.");
        } finally {
            HibernateSessionManager.close();
        }
    }

    @RequestMapping(value = "/listDocumentReviewNotes", method = RequestMethod.POST)
    @ResponseBody
    public Object listDocumentReviewNotes(@RequestBody @Valid final DocumentIdView request) {

        log.debug("listDocumentReviewNotes");

        DocumentReviewNoteListView result = new DocumentReviewNoteListView();

        try {
            HibernateSessionManager.start();

            UserPreferences prefs = getUserPreferences();

            Person pers = Person.findById(prefs.getUserId());
            Group adminGroup = null;

            for (Group group : pers.getGroups()) {
                adminGroup = group;
            }

            if (adminGroup == null) {
                return new ErrorView("Error retrieving document review notes.");
            }

            Role.initialize();
            if (pers.hasRole(Role.NDS_ADMIN) == false && pers.hasRole(Role.REVIEWER) == false
                    && pers.hasRole(Role.READ_ONLY_STAFF) == false && pers.hasRole(Role.SUPER_USER) == false) {
                return new ErrorView("Error retrieving document review notes.");
            }

            // List<DocumentReviewNote> noteList = DocumentReviewNote.listByReviewerAndDoc(prefs.getUserLoginId(),
            // request.getDocumentId(), adminGroup.getId());
            List<DocumentReviewNote> noteList = DocumentReviewNote.listByGroupId(request.getDocumentId(), adminGroup.getId());

            for (DocumentReviewNote note : noteList) {
                // add to result list
                DocumentReviewNoteView view = new DocumentReviewNoteView();
                view.setDocumentId(request.getDocumentId());
                view.setCreated(note.getCreatedOn() == null ? null : SDF.format(note.getCreatedOn()));
                view.setNote(note.getText());

                result.getNotes().add(view);
            }

            return result;
        } catch (Exception e) {
            log.error("Error retrieving document review notes.", e);
            HibernateSessionManager.rollback();
            return new ErrorView("Error retrieving document review notes.");
        } finally {
            HibernateSessionManager.close();
        }
    }

    @RequestMapping(value = "/addDocumentReviewNote", method = RequestMethod.POST)
    @ResponseBody
    public Object addDocumentReviewNote(@RequestBody @Valid final SaveDocumentReviewNoteView request) {

        log.debug("addDocumentReviewNote");

        Document doc = null;

        try {
            HibernateSessionManager.start();

            UserPreferences prefs = getUserPreferences();

            Person pers = Person.findById(prefs.getUserId());

            Group adminGroup = null;

            for (Group group : pers.getGroups()) {
                adminGroup = group;
            }

            doc = Document.findById(request.getDocumentId());
            if (doc == null) {
                log.error(
                        "Document id " + request.getDocumentId() + " does not exist.  Cannot save the document review status.");
                return new ErrorView("Error saving the document review status for documentId: " + request.getDocumentId());
            }

            if (adminGroup == null) {
                return new ErrorView("Error: User does not have permission to perform this action.");
            }

            Role.initialize();
            if (pers.hasRole(Role.NDS_ADMIN) == false && pers.hasRole(Role.REVIEWER) == false
                    && pers.hasRole(Role.READ_ONLY_STAFF) == false && pers.hasRole(Role.SUPER_USER) == false) {
                return new ErrorView("Error: User does not have permission to perform this action.");
            }

            DocumentReviewNote drn = DocumentReviewNote.create(doc, request.getNote(), prefs.getUserLoginId(), adminGroup);

            //
            // format and return the response
            DocumentReviewNoteView drnView = new DocumentReviewNoteView();
            drnView.setDocumentId(doc.getId());
            drnView.setNote(drn.getText());
            drnView.setCreated(drn.getCreatedOn() == null ? "" : SDF.format(drn.getCreatedOn()));
            drnView.setCreatedBy(pers.getFullName());
            drnView.setId(drn.getId());
            return drnView;

        } catch (Exception e) {
            log.error("Error saving document review notes.", e);
            HibernateSessionManager.rollback();
            return new ErrorView("Error saving document review notes.");
        } finally {
            HibernateSessionManager.close();
        }
    }

}
