package gov.va.vinci.dart.biz;

import gov.va.vinci.dart.common.ValidationHelper;
import gov.va.vinci.dart.common.exception.ObjectNotFoundException;
import gov.va.vinci.dart.common.exception.ValidationException;
import gov.va.vinci.dart.service.DartObjectFactory;
import gov.va.vinci.dart.wf2.WorkflowResolver;

import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorType;
import javax.persistence.DiscriminatorValue;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;

@Entity
@Table(name="activity",schema="hib")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(
	    name="type",
	    discriminatorType=DiscriminatorType.INTEGER
	)
@DiscriminatorValue("1")
// also 2 = ResearchStudy
// and 3 = OperationalStudy
public abstract class Activity extends BusinessObject {
	protected static final Calendar CAL_1900 = Calendar.getInstance();
	
	static {
		CAL_1900.set(1900, 0, 1);
	}

	@Temporal(TemporalType.DATE)
	@Column(name="startdate")
	protected Date startDate;
	
	@Temporal(TemporalType.DATE)
	@Column(name="enddate")
	protected Date endDate;

	@Column(name="officialname")
	protected String officialName;
	
	@OneToMany(fetch = FetchType.LAZY, mappedBy = "activity")
	protected Set<Request> requests;

	
	public static Activity findById(final int activityId) {
		return DartObjectFactory.getInstance().getActivityDAO().findById(activityId);
	}

	public Set<Request> getRequests() {
		return requests;
	}
	
	// TESTING ONLY
	public void setRequests(final Set<Request> requests) {
		this.requests = requests;
	}
	
	public Request getFirstRequest() {
		if (requests == null || requests.size() < 1) {
			return null;  // TODO- should throw an exception?
		}
		
		// find the first request that is not an amendment
		// (Sean Connery says "There can be only one.")
		for (Request request : requests) {
			if (request.isAmendment() == false) {
				return request;
			}
		}
		
		return null;
	}
	
	/** Get the current request for the activity
	 * 
	 * @return
	 */
	public Request getCurrentRequest() {
		if (requests == null || requests.size() < 1) {
			return null;  // TODO- should throw an exception?
		}
		
		// find the current request that is not an amendment
		for (Request request : requests) {
			if (request.isCurrent() == true) {
				return request;
			}
		}
		
		return null;
	}
	
	public Date getStartDate() {
		return startDate;
	}
	
	public Date getEndDate() {
		return endDate;
	}
	
	public String getOfficialName() {
		return officialName;
	}
	
	public void addRequest(final Request request) throws ValidationException {
		ValidationHelper.required("Request", request);
		
		requests.add(request);
	}

	public void validate(final List<String>msgs) {
		if (name == null) {
			msgs.add("Research Study Name is required");
		}
		else {
			if (name.length() < 1) {
				msgs.add("Research Study Name is required");
			}
	
			if (name.length() > 1024) {
				msgs.add("Research Study Name cannot be more than 1024 characters");
			}
		}

		if (officialName == null) {
			msgs.add("Research Study Official Name is required");
		}
		else {
			if (officialName.length() < 1) {
				msgs.add("Research Study Official Name is required");
			}
	
			if (officialName.length() > 1024) {
				msgs.add("Research Study Official Name cannot be more than 1024 characters");
			}
		}
		
		if (startDate == null) {
			msgs.add("Research Study Start Date is required");
		}
		else {
			if (CAL_1900.getTime().after(startDate)) {
				msgs.add("Research Study Start date must be after Jan 1, 1900");
			}
		}
		
		if (endDate == null) {
			msgs.add("Research Study End Date is required");
		}
		else {
			if (CAL_1900.getTime().after(endDate)) {
				msgs.add("Research Study End date must be after Jan 1, 1900");
			}
		}

		if (startDate != null && endDate != null) {
			if (startDate.after(endDate)) {
				msgs.add("Research Study Start date must be before end date.");
			}
		}
	}
	
	public void updateOfficialName(final String userId, final String newText) throws ValidationException {
		ValidationHelper.required("User Id", userId);
		ValidationHelper.required("Official Name", newText);
		ValidationHelper.validateSize("Official Name", newText, 1, 1024);

		officialName = newText;
		updatedBy = userId;
		updatedOn = new Date();
	}
	
	public void updateName(final String userId, final String newText) throws ValidationException {
		ValidationHelper.required("User Id", userId);
		ValidationHelper.required("Name", newText);
		ValidationHelper.validateSize("Name", newText, 1, 1024);

		name = newText;
		updatedBy = userId;
		updatedOn = new Date();
	}

	
	/**
	 * Returns true if the specified person is the request creator or a participant.
	 * @param person
	 * @return
	 */
	public boolean hasCreatorOrParticipant(final Person person) {

		boolean creatorOrParticipant = false;
		
		if( person != null ) {
			
			Set<Request> requestSet = getRequests();
			if( requestSet != null ) {

				for( Request currRequest : requestSet ) {
					if( currRequest != null ) {

						boolean currResult = currRequest.hasCreatorOrParticipant(person);
						if( currResult == true ) {
							creatorOrParticipant = currResult;	//remember if we ever saw a positive
						}//end if
					}//end if
				}//end for
			}//end if
					
		}//end if

		return creatorOrParticipant;
	}
	
	public boolean verifyReadAccessPermissions(final Person person) throws ObjectNotFoundException {

		if( person == null ) {
			return false;
		}

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

		
		if( person.hasRole(Role.SUPER_USER) == false && person.hasRole(Role.NDS_ADMIN) == false && person.hasRole(Role.REVIEWER) == false && person.hasRole(Role.READ_ONLY_STAFF) == false ) {
			return (hasCreatorOrParticipant(person));	//participant or the request creator?
		}

		
		if( person.hasRole(Role.SUPER_USER) ) {
			return true;
		}


		if( person.hasRole(Role.NDS_ADMIN) || 
			person.hasRole(Role.REVIEWER) || person.hasRole(Role.READ_ONLY_STAFF) ) {


			WorkflowTemplate workflowTemplate = null;
			Group reviewGroup = null;
			if( person.hasRole(Role.REVIEWER) || person.hasRole(Role.READ_ONLY_STAFF) ) {
				for( Group group : person.getGroups() ) {				 
					reviewGroup = group;  // grab the last group the user belongs to?  why?  Because we like it...
				}

			} else if( person.hasRole(Role.NDS_ADMIN) ) {
				workflowTemplate = WorkflowTemplate.findByGroupIdAndWorkflowTypeId(Group.NDS.getId(), WorkflowResolver.WF_NDS);
			}
			
			
			Set<Request> requestSet = getRequests();
			if( requestSet != null ) {
				for( Request req : requestSet ) {

					if( person.hasRole(Role.NDS_ADMIN) ) {	//NDS Admin
	
						if( req.getWorkflowTypeId() == WorkflowResolver.WF_RESEARCH_REQUEST  || req.getWorkflowTypeId() == WorkflowResolver.WF_PREPARATORY_REQUEST) {	//new request
	
							//is there an NDS workflow attached to this request?
							if( workflowTemplate != null ) {

								RequestWorkflow workflow = RequestWorkflow.findOpenByRequestAndWorkflowTemplateId( req.getId(), workflowTemplate.getId() );	//get the open NDS workflow if there is one
								if( workflow != null ) {
									return true;
								}//end if

							}//end if -- workflowTemplate
	
						} else {	//old request
							return true;
						}

					} else {	//Reviewer (or Read-only Staff for a Review Group)

						if( reviewGroup != null ) {
	
							//step through all of the reviews attached to this request and find all reviews for this group (for any workflow)
							List<Review> reviewsForGroup = Review.listByRequestIdAndGroupId( req.getId(), reviewGroup.getId() );
							if( reviewsForGroup != null ) {

								//if this is a new request, determine if the workflow is open or if it is covered by an open workflow
								for( Review review : reviewsForGroup ) {
									if( review != null ) {
		
										RequestWorkflow reviewWorkflow = review.getWorkflow();
										if( reviewWorkflow != null ) {
		
											if( reviewWorkflow.isClosed() == false ) {
												return true;
											}//end if
	
										} else {	//old request, so ignore the workflow
											return true;
										}//end else
	
									}//end if -- review
								}//end for
							}//end if -- reviewsForGroup
						}//end if -- reviewGroup

					}//end else
				}//end for

			}//end if
		}//end if
		
		return false;
	}
	
}
