package gov.va.vinci.dart;

import gov.va.vinci.dart.biz.Activity;
import gov.va.vinci.dart.biz.DartRequest;
import gov.va.vinci.dart.biz.Group;
import gov.va.vinci.dart.biz.Location;
import gov.va.vinci.dart.biz.Narrative;
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.Role;
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.AdminToolNarrativeView;
import gov.va.vinci.dart.json.GroupListView;
import gov.va.vinci.dart.json.LocationIdListView;
import gov.va.vinci.dart.json.LocationIdView;
import gov.va.vinci.dart.json.LocationListView;
import gov.va.vinci.dart.json.PersonDetailsView;
import gov.va.vinci.dart.json.PersonListView;
import gov.va.vinci.dart.json.PersonNameView;
import gov.va.vinci.dart.json.PersonQueryView;
import gov.va.vinci.dart.json.PersonView;
import gov.va.vinci.dart.json.RequestIdView;
import gov.va.vinci.dart.json.RoleAndGroupListView;
import gov.va.vinci.dart.json.RoleListView;
import gov.va.vinci.dart.json.SavePersonDetailsView;
import gov.va.vinci.dart.json.TrackingNumberView;
import gov.va.vinci.dart.json.builder.GroupListViewBuilder;
import gov.va.vinci.dart.json.builder.LocationListViewBuilder;
import gov.va.vinci.dart.json.builder.PersonDetailsViewBuilder;
import gov.va.vinci.dart.json.builder.PersonListViewBuilder;
import gov.va.vinci.dart.json.builder.RoleAndGroupListViewBuilder;
import gov.va.vinci.dart.json.builder.RoleListViewBuilder;
import gov.va.vinci.dart.rest.RestQuery;
import gov.va.vinci.dart.service.DartObjectFactory;
import gov.va.vinci.dart.usr.UserManager;
import gov.va.vinci.dart.usr.UserPreferences;
import gov.va.vinci.dart.wf2.WorkflowResolver;
import gov.va.vinci.security.userdetails.UserDetails;

import java.text.SimpleDateFormat;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;

import org.apache.commons.lang.StringUtils;
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.PathVariable;
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 AdminController 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(AdminController.class);
	
	@Autowired
	private PersonListViewBuilder personListViewBuilder;
	
	@Autowired
	private GroupListViewBuilder groupListViewBuilder;
	
	@Autowired
	private PersonDetailsViewBuilder personDetailsViewBuilder;
	
	@Autowired
	private RoleAndGroupListViewBuilder roleAndGroupListViewBuilder;
	
	@Autowired
	private WorkflowResolver workflowResolver;
	
	
	@RequestMapping(value = "/persons", method = RequestMethod.GET)
	@ResponseBody
	public Object listPersons(final HttpServletRequest request) throws Exception {
		log.debug("list persons ");
 
		String queryString = request.getQueryString();
		RestQuery q = new RestQuery(queryString);
		String term = q.getTerm("term");
		try {
			HibernateSessionManager.start();

			UserPreferences prefs = getUserPreferences();
			Person user = Person.findById(prefs.getUserId());

			Role.initialize();
			if (user.hasRole(Role.SUPER_USER) == false ) {
				return new ErrorView("User does not have the necessary permission to perform this action.");
			}//end if
			
			if (q.isEmpty() || StringUtils.isEmpty(term)) {
				List<Person> personList = Person.listAll();
				return personListViewBuilder.build(personList);
			}
			else {
				List<Person> personList = Person.list(term);
				return personListViewBuilder.build(personList);
			}
		} catch (Exception e) {
			log.error("Error retrieving comments.", e);
			HibernateSessionManager.rollback();
			throw e;
		} finally {
			HibernateSessionManager.close();
		}
	}
	
	@RequestMapping(value = "/findPersons", method = RequestMethod.POST)
	@ResponseBody
	public Object findPersons(@RequestBody @Valid final PersonQueryView request) {
		
		log.debug("find persons with key " + request.getKey());

		PersonListView result = new PersonListView();

		try {
			HibernateSessionManager.start();

			// call the LDAP search interface to Active Directory
			UserManager userManager = DartObjectFactory.getInstance().getUserManager();
			List<String[]> users = userManager.searchUsers(request.getKey());
			
			//TODO: don't add this person if their full name starts with vha? (then it isn't actually a person)
			for (String[] sary : users) {
				PersonView view = new PersonView();
				view.setId(-1);
				view.setName(sary[0]);
				view.setDescription(sary[1]);
				view.setFullName(sary[1]);
				result.getPersons().add(view);
			}
			
			return result;
			
		} catch (Exception e) {
			log.error("Error retrieving person data.", e);
			HibernateSessionManager.rollback();
			return new ErrorView("Error retrieving person data.");
		} finally {
			HibernateSessionManager.close();
		}
	}
	

	@RequestMapping(value = "/findRoleAndGroup", method = RequestMethod.POST)
	@ResponseBody
	public Object findRoleAndGroup(@RequestBody @Valid final PersonNameView view) {
		log.debug("findRoleAndGroup for " + view.getName());

		PersonDetailsView result = new PersonDetailsView();

		try {
			HibernateSessionManager.start();

			UserPreferences prefs = getUserPreferences();
			Person user = Person.findById(prefs.getUserId());

			Role.initialize();
			if (user.hasRole(Role.ADMINISTRATOR) == false ) {
				return new ErrorView("User does not have the necessary permission to perform this update.");
			}//end if


			Person person = null;

			try {
				person = Person.findByName( view.getName() );
			} catch (ObjectNotFoundException e) {
				// ignore here - handle in the next if block
			}

			if (person == null) {
				log.debug("Person " + view.getName() + " not found.");

				UserManager userManager = DartObjectFactory.getInstance().getUserManager();

				// get the participant's info from Active Directory
				UserDetails userDetails = userManager.getUserDetails( view.getName() );

				// person selected from the Active Directory should be created in our database.
				Location loc = Location.findById(1);  // TODO- get the correct location id
				person = Person.create( view.getName(), userDetails.getFullName(), loc );
			}

			if( person != null ) {
				result = personDetailsViewBuilder.build(person, false);
			}//end if

			return result;

		} catch (Exception e) {
			log.error("Error retrieving role and group details.", e);
			HibernateSessionManager.rollback();
			return new ErrorView("Error retrieving role and group details.");
		} finally {
			HibernateSessionManager.close();
		}
	}

	
	@RequestMapping(value = "/saveRoleAndGroup", method = RequestMethod.POST)
	@ResponseBody
	public Object saveRoleAndGroup(@RequestBody @Valid final SavePersonDetailsView view) {
		log.debug("saveRoleAndGroup for " + view.getName());

		PersonDetailsView result = new PersonDetailsView();

		try {
			HibernateSessionManager.start();

			UserPreferences prefs = getUserPreferences();
			Person user = Person.findById(prefs.getUserId());

			Role.initialize();
			if (user.hasRole(Role.ADMINISTRATOR) == false ) {
				return new ErrorView("User does not have the necessary permission to perform this update.");
			}//end if
			

			Person person = null;

			try {
				person = Person.findByName( view.getName() );
			} catch (ObjectNotFoundException e) {
				// ignore here - handle in the next if block
			}

			if (person == null) {
				log.debug("Person " + view.getName() + " not found.");

				UserManager userManager = DartObjectFactory.getInstance().getUserManager();

				// get the participant's info from Active Directory
				UserDetails userDetails = userManager.getUserDetails( view.getName() );

				// person selected from the Active Directory should be created in our database.
				Location loc = Location.findById(1);  // TODO- get the correct location id
				person = Person.create( view.getName(), userDetails.getFullName(), loc );
			}

			if( person != null ) {

				Group.initialize();
				Role.initialize();
				
				Set<Role> newRoles = new HashSet<Role>();
				Set<Group> newGroups = new HashSet<Group>();

				if( view != null ) {
					if( view.isJanitor() == true ) {	//Admin Tool
						newRoles.add( Role.ADMINISTRATOR );
					}//end if

//					if( view.getRoleAndGroupList().getRolesAndGroups() != null ) {
//						for( RoleAndGroupView roleAndGroupView : view.getRoleAndGroupList().getRolesAndGroups() ) {
//							if( roleAndGroupView != null ) {
								
								try {
									Group group = null;
									Role role = null;

									if( view.getGroupId() > 0 ) {	//requestor has no group
										group = Group.findById( view.getGroupId() );
									}//end if

									if( view.getRoleId() > 0 ) {	//requestor has no role
										role = Role.findById( view.getRoleId() );
									}//end if


									//verify that this is a valid role and group combo					
									if( group != null && role != null ) {
										Set<Role> validRoles = group.getSupportedRoles();
										if( validRoles != null && validRoles.contains(role) ) {
											newGroups.add( group );
											newRoles.add( role );
										} else {
											log.error("Invalid group: " + view.getGroupId() + " and role: " + view.getRoleId());
											return new ErrorView("Invalid group: " + view.getGroupId() + " and role: " + view.getRoleId());
										}
									}//end if

								} catch( ObjectNotFoundException exc ) {
									log.error("Invalid group: " + view.getGroupId() + " and role: " + view.getRoleId());
									return new ErrorView("Invalid group: " + view.getGroupId() + " and role: " + view.getRoleId());
								}
								
								
//							}//end if
//						}//end for
//					}//end if

				}//end if

				//update the group and role info (remove all groups and roles, and keep only the specified values)
				person.modifyRoles(newRoles);
				person.modifyGroups(newGroups);


				result = personDetailsViewBuilder.build(person, false);
			}//end if -- person

			return result;

		} catch (Exception e) {
			log.error("Error updating group and role.", e);
			HibernateSessionManager.rollback();
			return new ErrorView("Error updating group and role.");
		} finally {
			HibernateSessionManager.close();
		}
	}
	

	@RequestMapping(value = "/getAllRolesAndGroups", method = RequestMethod.GET)
	@ResponseBody
	public Object getAllRolesAndGroups(final HttpServletRequest request) {
		log.debug("getAllRolesAndGroups");
		
		try {
			HibernateSessionManager.start();

			RoleAndGroupListView result = new RoleAndGroupListView();

			result = roleAndGroupListViewBuilder.buildAllRolesAndGroups();	//get all possible roles and groups

			return result;
			
		} catch (Exception e) {
			log.error("Error retrieving roles and groups.", e);
			HibernateSessionManager.rollback();
			return new ErrorView("Error retrieving roles and groups.");
		} finally {
			HibernateSessionManager.close();
		}
	}
	
	
	@RequestMapping(value = "/persons/{personId}", method = RequestMethod.GET)
	@ResponseBody
	public Object getPersonDetails(@PathVariable @Valid final int personId) throws Exception {
		log.debug("get person details with id " + personId);

		try {
			HibernateSessionManager.start();

			UserPreferences prefs = getUserPreferences();
			Person user = Person.findById(prefs.getUserId());

			Role.initialize();
			if (user.hasRole(Role.SUPER_USER) == false ) {
				return new ErrorView("User does not have the necessary permission to perform this update.");
			}//end if
			
			Person pers = Person.findById(personId);
			return personDetailsViewBuilder.build(pers, true);
		} catch (Exception e) {
			log.error("Error retrieving participant data.", e);
			HibernateSessionManager.rollback();
			throw e;
		} finally {
			HibernateSessionManager.close();
		}
	}

	@RequestMapping(value = "/groups", method = RequestMethod.GET)
	@ResponseBody
	public GroupListView listGroups(final HttpServletRequest request) throws Exception {
		log.debug("list groups ");
 
		try {
			HibernateSessionManager.start();
			Group.initialize();

			List<Group> groupList = Group.listAll();
			return groupListViewBuilder.build(groupList, true);
		} catch (Exception e) {
			log.error("Error retrieving groups.", e);
			HibernateSessionManager.rollback();
			throw e;
		} finally {
			HibernateSessionManager.close();
		}
	}
	
	@RequestMapping(value = "/roles", method = RequestMethod.GET)
	@ResponseBody
	public RoleListView listRoles(final HttpServletRequest request) throws Exception {
		log.debug("list roles ");
 
		try {
			HibernateSessionManager.start();

			List<Role> roleList = Role.listAll();
			return new RoleListViewBuilder().build(roleList);
		} catch (Exception e) {
			log.error("Error retrieving roles.", e);
			HibernateSessionManager.rollback();
			throw e;
		} finally {
			HibernateSessionManager.close();
		}
	}

	@RequestMapping(value = "/locations", method = RequestMethod.GET)
	@ResponseBody
	public LocationListView listLocations(final HttpServletRequest request) throws Exception {
		log.debug("list locations ");
 
		try {
			HibernateSessionManager.start();

			List<Location> locationList = Location.listAll();
			return new LocationListViewBuilder().build(locationList);
		} catch (Exception e) {
			log.error("Error retrieving locations.", e);
			HibernateSessionManager.rollback();
			throw e;
		} finally {
			HibernateSessionManager.close();
		}
	}

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

		log.debug("list all locations");

		//List<String> stringList = new ArrayList<String>();
		LocationIdListView locationList = new LocationIdListView();
		
		try {
			HibernateSessionManager.start();

			for (Location loc : Location.listAll()) {
				//stringList.add(loc.getName());
				
				LocationIdView locView = new LocationIdView();
				locView.setId( loc.getId() );
				locView.setName( loc.getName() );
				
				locationList.getLocations().add( locView );
			}
	
			//return new LocationListView(stringList);
			return locationList;
			
		} catch (Exception e) {
			log.error("Error retrieving locations.", e);
			HibernateSessionManager.rollback();
			return new ErrorView("Error retrieving locations.");
		} finally {
			HibernateSessionManager.close();
		}
	}
	
	/*************** Administration Tool Modal ***************/
	
	@RequestMapping(value = "/getRequestProperties", method = RequestMethod.POST)
	@ResponseBody
	public Object getRequestProperties(@RequestBody @Valid final TrackingNumberView trackingNumber) {
		
		log.debug("get properties");

		AdminToolNarrativeView responseView = new AdminToolNarrativeView();

		try {
			HibernateSessionManager.start();
			
			UserPreferences prefs = getUserPreferences();
			
			Person user = Person.findById(prefs.getUserId());
			Role.initialize();
			
			if (user.hasRole(Role.ADMINISTRATOR)) {
					
				Request request = null;
				try {
					request = Request.findByTrackingNumber(trackingNumber.getTrackingNumber());
				} catch( ObjectNotFoundException e ) {
					log.error("Error retrieving request.", e);
					return new ErrorView("Error retrieving request.");
				}
				
				if( request != null ) {

					responseView.setRequestId( request.getId() );	//requestID that corresponds to this tracking number


					Activity activity = request.getActivity();
					
					if( activity != null ) {
						responseView.setOfficialName(activity.getOfficialName());	//get the official name
						responseView.setShortName(activity.getName());
					}//end if
					
					
					responseView.setAmendment( request.isAmendment() );		//is this an amendment?
					
					if( request.isAmendment() ) {	//is this an amendment? (has a narrative)
						List<Narrative> narrativeList = request.getNarratives();	//get the narratives associated with this request
						if( narrativeList != null ) {
							for( Narrative narrative : narrativeList ) {
								if( narrative != null ) {
									responseView.setNarrativeText(narrative.getText());	//get the narrative text
								}//end if
							}//end for
						}//end if
					}
					
					
					//get the request type (for intermediate reviews)
					if( DartRequest.class.isAssignableFrom(request.getClass()) ) {
						responseView.setResearchRequest(true);
					} else if( PreparatoryRequest.class.isAssignableFrom(request.getClass()) ) {
                        responseView.setPreparatoryRequest(true);
                    } else if( OperationalRequest.class.isAssignableFrom(request.getClass()) ) {
						responseView.setOperationsRequest(true);
					}
				}//end if
			}//end if -- ADMINISTRATOR

			return responseView;		
			
		} catch (Exception e) {
			log.error("Error retrieving request properties.", e);
			HibernateSessionManager.rollback();
			return new ErrorView("Error retrieving request properties.");
		} finally {
			HibernateSessionManager.close();
		}
	}
	
	
	@RequestMapping(value = "/setNarrativeText", method = RequestMethod.POST)
	@ResponseBody
	public Object setNarrativeText(@RequestBody @Valid final TrackingNumberView trackingNumber) {
		
		log.debug("change Narrative");
		
		AdminToolNarrativeView responseView = new AdminToolNarrativeView();
		
		try {
			HibernateSessionManager.start();
			
			UserPreferences prefs = getUserPreferences();
			
			Person user = Person.findById(prefs.getUserId());
			Role.initialize();
			
			Request request = null;
						
			if (user.hasRole(Role.ADMINISTRATOR)) {

				try {
					request = RequestController.retrieveRequest(trackingNumber.getRequestId());
			
				} catch (ObjectNotFoundException e) {
					log.error("Error loading request " + trackingNumber.getRequestId(), e);
					return new ErrorView("Error loading request " + trackingNumber.getRequestId());
				}

				if( request != null ) {	
					
					if( request.isAmendment() ) {	//is this an amendment?
						
						List<Narrative> narrativeList = request.getNarratives();	//get the narratives associated with this request
						if( narrativeList != null ) {
							for( Narrative narrative : narrativeList ) {
								if( narrative != null ) {
									narrative.updateText(prefs.getUserLoginId(), trackingNumber.getChangeNarrative());	//update the narrative text
									responseView.setNarrativeText(narrative.getText());	   //get updated narrative text								
								}//end if
							}//end for
						}//end if
						
					}//end if

				}//end if
			}//end if
		
			return responseView;
		
		} catch (Exception e) {
			log.error("Error setting narrative text.", e);
			HibernateSessionManager.rollback();
			return new ErrorView("Error setting narrative text.");
		} finally {
			HibernateSessionManager.close();
		}		
	}
	
	
	@RequestMapping(value = "/setOfficialName", method = RequestMethod.POST)
	@ResponseBody
	public Object setOfficialName(@RequestBody @Valid final TrackingNumberView trackingNumber) {
		
		log.debug("change Official Name");
		
		AdminToolNarrativeView responseView = new AdminToolNarrativeView();
		
		try {
			
			HibernateSessionManager.start();
			
			UserPreferences prefs = getUserPreferences();
			
			Person user = Person.findById(prefs.getUserId());
			Role.initialize();
			
			Request request = null;
			
			if (user.hasRole(Role.ADMINISTRATOR)) {

				try {
					request = RequestController.retrieveRequest(trackingNumber.getRequestId());	//find the request by ID
			
				} catch (ObjectNotFoundException e) {
					log.error("Error loading request " + trackingNumber.getRequestId(), e);
					return new ErrorView("Error loading request " + trackingNumber.getRequestId());
				}
				

				if( request != null ) {
					Activity activity = request.getActivity();
					if (activity != null) {
						activity.updateOfficialName(prefs.getUserLoginId(), trackingNumber.getChangeofficialName());	//update the official name
						responseView.setOfficialName(activity.getOfficialName());
					}
				}
			}

			return responseView;

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

	@RequestMapping(value = "/setShortName", method = RequestMethod.POST)
	@ResponseBody
	public Object setShortName(@RequestBody @Valid final TrackingNumberView trackingNumber) {
		log.debug("set Short Name");
		
		AdminToolNarrativeView responseView = new AdminToolNarrativeView();
		
		try {
			
			HibernateSessionManager.start();
			
			UserPreferences prefs = getUserPreferences();
			
			Person user = Person.findById(prefs.getUserId());
			Role.initialize();
			
			Request request = null;
			
			if (user.hasRole(Role.ADMINISTRATOR)) {

				try {
					request = RequestController.retrieveRequest(trackingNumber.getRequestId());	//find the request by ID
			
				} catch (ObjectNotFoundException e) {
					log.error("Error loading request " + trackingNumber.getRequestId(), e);
					return new ErrorView("Error loading request " + trackingNumber.getRequestId());
				}


				if( request != null ) {
					request.updateName(prefs.getUserLoginId(), trackingNumber.getChangeShortName());

					Activity activity = request.getActivity();
					if (activity != null) {
						activity.updateName(prefs.getUserLoginId(), trackingNumber.getChangeShortName());
						responseView.setShortName(activity.getName());
					}
				}
			}

			return responseView;

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