package gov.va.vinci.dart;

import gov.va.vinci.dart.biz.DartRequest;
import gov.va.vinci.dart.biz.Group;
import gov.va.vinci.dart.biz.GroupTask;
import gov.va.vinci.dart.biz.OperationalRequest;
import gov.va.vinci.dart.biz.Person;
import gov.va.vinci.dart.biz.PersonTask;
import gov.va.vinci.dart.biz.PreparatoryRequest;
import gov.va.vinci.dart.biz.Request;
import gov.va.vinci.dart.biz.RequestWorkflow;
import gov.va.vinci.dart.biz.Role;
import gov.va.vinci.dart.biz.Task;
import gov.va.vinci.dart.biz.TaskAging;
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.json.TaskAgingView;
import gov.va.vinci.dart.json.TaskIdView;
import gov.va.vinci.dart.json.TaskListView;
import gov.va.vinci.dart.json.TaskView;
import gov.va.vinci.dart.json.UserIdView;
import gov.va.vinci.dart.usr.UserPreferences;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashSet;
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.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;

@Controller
public class TaskController extends DartController {
	
	public static final String VINCI_SERVICES_USER_ID = "123";
	public static final SimpleDateFormat SDF = new SimpleDateFormat("MM/dd/yyyy");
	private static Log log = LogFactory.getLog(TaskController.class);
	
	@RequestMapping(value = "/getUserTasks", method = RequestMethod.POST)
	@ResponseBody
	public Object getUserTasks(@RequestBody @Valid final UserIdView userIdView) {

		log.debug("getUserTasks");
		
		if (userIdView == null) {
			return new ErrorView("Error loading user task list");
		}

		try {
			HibernateSessionManager.start();

			UserPreferences prefs = getUserPreferences();
			
			TaskListView result = new TaskListView();
			
			Person person = null;
			try {
				person = Person.findById(prefs.getUserId());
			} catch (ObjectNotFoundException e) {
				throw new ObjectNotFoundException("Cannot find person with id: " + prefs.getUserId());
			}
			Role.initialize();
			
			//
			// superuser can view all tasks (but cannot edit)
			if( person.hasRole(Role.SUPER_USER) ) {
				
				//
				//person tasks (super-user can see all individual tasks)
				List<PersonTask> tlist = PersonTask.listAllOpenPersonTasks();	//get the open tasks
				List<TaskView> personTaskList = populatePersonTaskViewList( tlist );
				result.getTasks().addAll( personTaskList );
				
				//
				//group tasks (super-user can see the tasks for all groups)
				Set<Group> groupSet = new HashSet<Group>();
				groupSet.addAll( Group.listAll() );
				List<TaskView> groupTaskList = populateUniqueGroupTaskViewList( groupSet );	//get the tasks for all groups
				result.getTasks().addAll( groupTaskList );
				
				
			} else {	//NOT a super-user, so get the regular task lists

				//get GroupTasks or PersonTasks, based on the current role
				if( person.hasRole(Role.NDS_ADMIN) == false && person.hasRole(Role.REVIEWER) == false && person.hasRole(Role.READ_ONLY_STAFF) == false ) {

					//
					//person tasks
					List<PersonTask>tlist = PersonTask.listOpenByOwner(prefs.getUserId());
					List<TaskView> personTaskList = populatePersonTaskViewList( tlist );
					result.getTasks().addAll( personTaskList );

				}//end if -- role
				
				
				//
				//group tasks (keep unique requestId for each group)
				List<TaskView> groupTaskList = populateUniqueGroupTaskViewList( person.getGroups() );	//get the tasks for the groups that this person belongs to
				result.getTasks().addAll( groupTaskList );
				
			}//end else -- NOT a super-user
			
			return result;

		} catch (Exception e) {
			log.error("Error loading user task list", e);
			HibernateSessionManager.rollback();
			return new ErrorView("Error loading user task list");
		} finally {
			HibernateSessionManager.close();
		}
	}
	
	
	/**
	 * Returns a list of Group TaskView objects (unique requestId for each group)
	 * @param taskList
	 * @return
	 * @throws ObjectNotFoundException 
	 */
	private List<TaskView> populateUniqueGroupTaskViewList( final Set<Group> groupList ) throws ObjectNotFoundException {
		
		List<TaskView> taskViewList = new ArrayList<TaskView>();

		// tempList for requestIds
		// TODO - this really should be done via DISTINCT in the JPQL query...
		ArrayList <String> tempList = new ArrayList<String>();
		String rid = null;
		for (Group group : groupList) {
			tempList.clear(); //clear the list
			
			List<GroupTask>glist = GroupTask.listOpenByOwner(group.getId());	//get the open tasks for this group

			// loop on the task list adding them to a JSON view
			for (Task task : glist) {
				if (task.getCompletedOn() == null) {
					Request request = task.getRequest();
	
					if (request == null) {
						log.error("Error : null request found for task id " + task.getId());
					} 
					else {
						rid= Integer.toString(request.getId());
						if (!tempList.contains(rid)) {
							taskViewList.add(populateTaskView(request, task, true));	//group tasks are for reviewers (could base this on the GroupTask type)
							tempList.add(rid);
						}
						
					}
				}
			}

		}		
		
		return taskViewList;
	}

	/**
	 * Returns a list of Person TaskView objects
	 * @param tlist
	 * @return
	 * @throws ObjectNotFoundException 
	 */
	private List<TaskView> populatePersonTaskViewList( final List<PersonTask> tlist ) throws ObjectNotFoundException {
		
		List<TaskView> taskViewList = new ArrayList<TaskView>();
		
		// loop on the task list adding them to a JSON view
		for (Task task : tlist) {
			if (task.getCompletedOn() == null) {
				Request request = task.getRequest();

				if (request == null) {
					log.error("Error : null request found for task id " + task.getId());
				} 
				else {
					taskViewList.add(populateTaskView(request, task, false));
				}
			}
		}
		
		return taskViewList;
	}
	
	private TaskView populateTaskView(final Request request, final Task task, final boolean isReview) throws ObjectNotFoundException {
		TaskView view = new TaskView();

		view.setTaskId(task.getId());
		view.setTrackingNumber(request.getTrackingNumber());
		view.setDateReceived(SDF.format(task.getCreatedOn()));
		view.setActivityName(request.getName());
		
		//set the request type, based on the workflow type
		if (DartRequest.class.isAssignableFrom(request.getClass())) {
			view.setActivityType(Request.DATA_ACCESS);			
			view.setResearchRequest(true);
		}else if (PreparatoryRequest.class.isAssignableFrom(request.getClass())) {
            view.setActivityType(Request.PREPARATORY_TO_RESEARCH_ACCESS);           
            view.setPreparatoryRequest(true);            
        }
		else if (OperationalRequest.class.isAssignableFrom(request.getClass())) {
			view.setActivityType(Request.OPERATIONS_DATA_ACCESS);			
			view.setOperationsRequest(true);			
		}

		//
		// get the Primary Investigator
		view.setContact( request.getPrincipalInvestigator() );
		
		view.setDescription(task.getDescription());
		view.setActivityId(request.getActivity().getId());
		view.setRequestId(request.getId());
		view.setReviewId(task.getReview()==null?0:task.getReview().getId());					
		
		view.setReviewTask(isReview);	//is this a review task? (group or person task)
		
		RequestWorkflow workflow = task.getWorkflow();
		if( workflow != null ) {
			view.setWorkflowId(workflow.getId());	//use the workflow associated with this task
		} else {
			view.setWorkflowId(0);	//use the top-level workflow
		}
		
		return view;
	}
	
	// this method actually does not examine the passed user id
	@RequestMapping(value = "/getTaskAging", method = RequestMethod.POST)
	@ResponseBody
	public Object getTaskAging(@RequestBody @Valid final UserIdView userIdView) {
		
		log.debug("getTaskAging");

		if (userIdView == null) {
			return new ErrorView("Error retrieving task data.");
		}
		
		try {
			HibernateSessionManager.start();

			UserPreferences prefs = getUserPreferences();
			
			TaskAging aging = Task.getTaskAging(prefs.getUserId());
			
			TaskAgingView result = new TaskAgingView(aging.getDay0(), aging.getDay10(), aging.getDay20(), aging.getDay30());
			
			return result;
			
		} catch (Exception e) {
			log.error("Error retrieving task data.", e);
			HibernateSessionManager.rollback();
			return new ErrorView("Error retrieving task data.");
		} finally {
			HibernateSessionManager.close();
		}
	}
	
	@RequestMapping(value = "/completeTask", method = RequestMethod.POST)
	@ResponseBody
	public Object completeTask(@RequestBody @Valid final TaskIdView taskIdView) {

		log.debug("completeTask " + taskIdView.getTaskId());

		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.SUPER_USER) == false ) {
				return new ErrorView("Error: User does not have permission to perform this action.");
			}

			Task task = Task.findById(taskIdView.getTaskId());
		
			if (task == null) {
				return new ErrorView("Error: Task " + taskIdView.getTaskId() + " not found.");
			}
			
			task.complete(prefs.getUserLoginId());
			
		} catch (Exception e) {
			log.error("Error completing task.", e);
			HibernateSessionManager.rollback();
			e.printStackTrace();
			return new ErrorView("Error completing task.");
		} finally {
			HibernateSessionManager.close();
		}
		return new ErrorView("OK");
	}

}
