package gov.va.vinci.dart.db.mock;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.List;
//import java.util.Set;

import gov.va.vinci.dart.common.exception.ObjectNotFoundException;
import gov.va.vinci.dart.biz.Comment;
import gov.va.vinci.dart.biz.Event;
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.OnlineData;
import gov.va.vinci.dart.biz.Participant;
import gov.va.vinci.dart.biz.Person;
import gov.va.vinci.dart.biz.Request;
import gov.va.vinci.dart.biz.RequestLocationDocumentSummary;
import gov.va.vinci.dart.biz.RequestParticipantDocumentSummary;
import gov.va.vinci.dart.biz.RequestStatus;
import gov.va.vinci.dart.biz.RequestSummary;
import gov.va.vinci.dart.biz.WorkflowSummary;
//import gov.va.vinci.dart.biz.RequestWorkflow;
import gov.va.vinci.dart.biz.ResearchStudy;
import gov.va.vinci.dart.biz.Review;
//import gov.va.vinci.dart.biz.WorkflowTemplate;
import gov.va.vinci.dart.db.RequestDAO;
import gov.va.vinci.dart.dms.biz.Document;
import gov.va.vinci.dart.service.DartObjectFactory;

public class RequestDAOMock implements RequestDAO {
	
	private static boolean initialized = false;
	private static int idCounter = 1;

	@Override
	public Request findById(int requestId) throws ObjectNotFoundException {
		initializeRequestHash();

		Request result = DartObjectFactory.getInstance().getDartRequestDAO().findById(requestId);
		
		if (result == null) {
			result = DartObjectFactory.getInstance().getOperationalRequestDAO().findById(requestId);
		}
		
		return result;
	}

	@Override
	public synchronized void save(Request request) {
		if (request == null) {
			throw new IllegalArgumentException();
		}
		
		if (request.getId() == 0) {
			request.setId(idCounter++);
		}
		
		if (request.getParticipants() == null) {
			request.setTestParticipants(new HashSet<Participant>());
		}

//		if (request.getReviews() == null) {
		if (request.getAllReviews() == null) {
			request.setReviews(new HashSet<Review>());
		}

		if (request.getDocuments() == null) {
			request.setDocuments(new HashSet<Document>());
		}

		if (request.getEvents() == null) {
			request.setEvents(new ArrayList<Event>());
		}

		if (request.getNarratives() == null) {
			request.setNarratives(new ArrayList<Narrative>());
		}

		if (request.getSites() == null) {
			request.setSites(new HashSet<Location>());
		}

		if (request.getOnlineDatas() == null) {
			request.setOnlineDatas(new HashSet<OnlineData>());
		}
		
		if (request.getComments() == null) {
			request.setComments(new HashSet<Comment>());
		}

	}

	public List<Request> listAll() {
		initializeRequestHash();
		
		List<Request> result = new ArrayList<Request>();
		
		result.addAll(DartObjectFactory.getInstance().getDartRequestDAO().listAll());
		result.addAll(DartObjectFactory.getInstance().getOperationalRequestDAO().listAll());
		
		return result;
	}
	
	@Override
	public int countRequestsInMonth() {
		// stop a stack overflow
		if (!initialized) {
			return 0;
		}
		
		initializeRequestHash();

		GregorianCalendar gc = new GregorianCalendar();
		int month = gc.get(Calendar.MONTH);  // month starts counting from 0

		int result = 0;
		
		for (Request req : listAll()) {
			gc.setTime(req.getCreatedOn());
			if (gc.get(Calendar.MONTH) == month) {
				result++;
			}
		}
		
		return result;
	}

	@Override
	public int countRequestAmendments(int requestId) {
		initializeRequestHash();
		
		int result = 0;
		
		for (Request req : listAll()) {
			if (req.isAmendment() && req.getHeadId() == requestId) {
				result++;
			}
		}
		
		return result;
	}

	@Override
	public List<RequestSummary> listByTrackingNumber(String trackingNumber) {
		initializeRequestHash();
		
		List<RequestSummary> result = new ArrayList<RequestSummary>();
		
		for (Request req : listAll()) {
			if (req.getTrackingNumber() != null && req.getTrackingNumber().indexOf(trackingNumber) >= 0) {
				RequestSummary rs = new RequestSummary();
				rs.setActivityId(req.getActivity().getId());
				rs.setRequestId(req.getId());
				result.add(rs);
			}
		}
		
		return result;
	}

	@Override
	public List<RequestLocationDocumentSummary> listRequestLocationDocumentSummaryByReviewer(
			int requestId, String reviewer) {
		initializeRequestHash();
		
		return new RequestLocationDocumentSummaryDAOMock(requestId, reviewer).build();
	}

	@Override
	public List<RequestParticipantDocumentSummary> listRequestParticipantDocumentSummaryByReviewer(
			int requestId, String reviewer) {
		initializeRequestHash();
		
		return new RequestParticipantDocumentSummaryDAOMock(requestId, reviewer).build();
	}

	private RequestSummary buildMockRequestSummary(Request req) {
		RequestSummary rs = new RequestSummary();
		WorkflowSummary workflowSummary = new WorkflowSummary();
		
		rs.setRequestId(req.getId());
		rs.setActivityId(req.getActivity().getId());
		rs.setTrackingNumber(req.getTrackingNumber());

		String aName = req.getActivity().getName();
		if (aName == null || aName.length() < 1) {
			rs.setActivityName(req.getActivity().getOfficialName());
		}
		else {
			rs.setActivityName(aName);
		}

		rs.setSubmittedOn(req.getSubmittedOn());
		rs.setUpdatedOn(new java.sql.Timestamp(req.getUpdatedOn().getTime()));

		// totalReviews
		rs.setTotalReviews(req.getAllReviews().size());
		
		int completedCount = 0;
		// completedReviews
		for (Review rev : req.getAllReviews()) {
			if (rev.isApproved() || rev.isRejected()) {
				completedCount++;
			}
		}
		rs.setCompletedReviews(completedCount);

		if (ResearchStudy.class.isAssignableFrom(req.getActivity().getClass())) {
			rs.setType(2);
			rs.setRequestType("Research Data Access");
			rs.setResearchRequest(true);
		}
		else {
			rs.setType(3);
			rs.setRequestType("Operations Data Access");
			rs.setOperationsRequest(true);
		}

		// TODO- need to update the status string
		try {
			workflowSummary.setStatus(req.getStatus().getId());	//TODO: may want to set multiple workflow-level status values
			
			RequestStatus reqSt = RequestStatus.findById(workflowSummary.getStatus());
			workflowSummary.setRequestState(reqSt.getName());
		} catch (ObjectNotFoundException e) {
			e.printStackTrace();
		}

		if (req.getPrincipalInvestigator() != null) {
			rs.setPrincipalInvestigatorName(req.getPrincipalInvestigator());
			rs.setRetrievedPrincipalInvestigatorName(true);
		}

		if (workflowSummary.getStatus() == RequestStatus.INITIATED.getId()
			|| workflowSummary.getStatus() == RequestStatus.CHANGE_REQUESTED.getId()) {
			rs.setEditable(true);
		}

		if (workflowSummary.getStatus() == RequestStatus.APPROVED.getId()) {	//TODO: may want to set multiple workflow-level status values (or use "completed"?)
			rs.setAmendable(true);
		}

		rs.setDaysElapsed(12);
		

		rs.getWorkflowSummaryList().add( workflowSummary );
		return rs;
	}
	
	@Override
	public List<RequestSummary> listAllRequestSummary() {
		initializeRequestHash();
		
		List<RequestSummary> result = new ArrayList<RequestSummary>();
		
		for (Request req : listAll()) {
			result.add(buildMockRequestSummary(req));
		}
		
		return result;
	}

	@Override
	public List<RequestSummary> listAllRequestSummaryByParticipant(final String participantId) {
		initializeRequestHash();
		
		List<RequestSummary> result = new ArrayList<RequestSummary>();
		
		for (Request req : listAll()) {
			result.add(buildMockRequestSummary(req));
		}
		
		return result;
	}

	@Override
	public List<RequestSummary> listAllRequestSummaryWithoutContactName() {
		initializeRequestHash();
		
		List<RequestSummary> result = new ArrayList<RequestSummary>();
		
		for (Request req : listAll()) {
			result.add(buildMockRequestSummary(req));
		}
		
		return result;
	}

	@Override
	public List<RequestSummary> listAllButInitiated() {
		initializeRequestHash();
		
		List<RequestSummary> result = new ArrayList<RequestSummary>();
		
		for (Request req : listAll()) {
			try {
				if (req.getStatus().getId() != RequestStatus.INITIATED.getId()) {
					result.add(buildMockRequestSummary(req));
				}
			}
			catch (ObjectNotFoundException e) {
				e.printStackTrace();
			}
		}
		
		return result;
	}

	@Override
	public List<RequestSummary> listAllButInitiatedWithoutContactName() {
		initializeRequestHash();
		
		List<RequestSummary> result = new ArrayList<RequestSummary>();
		
		for (Request req : listAll()) {
			try {
				if (req.getStatus().getId() != RequestStatus.INITIATED.getId()) {
					result.add(buildMockRequestSummary(req));
				}
			}
			catch (ObjectNotFoundException e) {
				e.printStackTrace();
			}
		}
		
		return result;
	}

	@Override
	public List<RequestSummary> listAllUserReviewable(int userId) {
		initializeRequestHash();
		
		List<RequestSummary> result = new ArrayList<RequestSummary>();
		
		for (Request req : listAll()) {
//			for (Review review : req.getReviews()) {
			for (Review review : req.getAllReviews()) {
				Group reviewer = review.getReviewer();
				for (Person pers : reviewer.getMembers()) {
					if (pers.getId() == userId) {
						result.add(buildMockRequestSummary(req));
					}
				}
			}
		}
		
		return result;
	}

	@Override
	public List<RequestSummary> listAllGroupReviewable_old(int groupId) {
		initializeRequestHash();
		
		List<RequestSummary> result = new ArrayList<RequestSummary>();
		
		for (Request req : listAll()) {
//			for (Review review : req.getReviews()) {
			for (Review review : req.getAllReviews()) {
				Group reviewer = review.getReviewer();
				if (reviewer.getId() == groupId) {
					result.add(buildMockRequestSummary(req));
				}
			}
		}
		
		return result;
	}

	@Override
	public List<RequestSummary> listAllGroupReviewable(String groupName) {
		initializeRequestHash();
		
		List<RequestSummary> result = new ArrayList<RequestSummary>();
		
		for (Request req : listAll()) {
//			for (Review review : req.getReviews()) {
			for (Review review : req.getAllReviews()) {
				Group reviewer = review.getReviewer();
				if (reviewer.getName().equalsIgnoreCase( groupName )) {
					result.add(buildMockRequestSummary(req));
				}
			}
		}
		
		return result;
	}
	
	/*---------------------------------------------------------------------------------------------------------------------------*/
	@Override
	public List<Request> listAllRequestByHeadId(int headId) {
		initializeRequestHash();
		
		List<Request> result = new ArrayList<Request>();
		
		for (Request req : listAll()) {
			if( req.getId() == headId || req.getHeadId() == headId ) {
				result.add(req);
			}
		}		
		return result;
	}

	
	@Override
	public List<Request> listAllPreviousRequests(final int headId, final Date createdOn) {
		initializeRequestHash();

		
		if( createdOn == null || headId == 0 )	//verify that we have a non-null createdOn date and that this is an amendment 
			return null;
		
		
		List<Request> result = new ArrayList<Request>();
		
		for (Request req : listAll()) {
			if( req.getId() == headId || req.getHeadId() == headId ) {
				if( createdOn != null && req.getCreatedOn() != null && req.getCreatedOn().before( createdOn ) ) {
					result.add(req);
				}
			}
		}

		Collections.sort( result, Request.getComparator() );	//sort, most recent first
		
		return result;
	}

	
	@Override
	public List<Request> listAllPreviousRequests(final int headId, final String trackingNumberStr) {
		initializeRequestHash();

		
		if( trackingNumberStr == null || headId == 0 )	//verify that we have a non-null tracking number and that this is an amendment 
			return null;
		
		
		List<Request> result = new ArrayList<Request>();
		
		for (Request req : listAll()) {
			if( req.getId() == headId || req.getHeadId() == headId ) {
				if( trackingNumberStr != null && req.getTrackingNumber() != null && req.getTrackingNumber().trim().toLowerCase().compareTo( trackingNumberStr.trim().toLowerCase() ) < 0 ) {
					result.add(req);
				}
			}
		}

		Collections.sort( result, Request.getDescTrackingNumberComparator() );	//sort, most recent tracking number first
		
		return result;
	}

	
	@Override
	public List<Integer> listAllPreviousRequestIds(final int headId, final String trackingNumberStr) {
		List<Integer> result = new ArrayList<Integer>();
		
		List<Request> previousRequestList = listAllPreviousRequests(headId, trackingNumberStr);
		if( previousRequestList != null ) {
			for( Request req : previousRequestList ) {
				result.add(req.getId());	//keep the request ID
			}
		}
		
		return result;
	}

	
//	@Override
//	public Request getPreviousRequestByStatus(final int headId, final String trackingNumberStr, final RequestStatus status) {
//		initializeRequestHash();
//		
//		List<Request> result = new ArrayList<Request>();
//		
//		for (Request req : listAll()) {
//			if( req.getId() == headId || req.getHeadId() == headId ) {
//				if( trackingNumberStr != null && req.getTrackingNumber() != null && req.getTrackingNumber().trim().toLowerCase().compareTo( trackingNumberStr.trim().toLowerCase() ) < 0 ) {
//					result.add(req);
//				}
//			}
//		}
//
//		if( result != null && result.size() > 0 ) {
//			Collections.sort( result, Request.getDescTrackingNumberComparator() );	//sort, most recent tracking number first
//			
//			for( Request prevReq : result ) {
//				try {
//					if( prevReq.getStatus().getId() == status.getId() )
//						return prevReq;
//
//				} catch (ObjectNotFoundException e) {
//					e.printStackTrace();
//					return null;
//				}
//			}
//		}		
//		
//		return null;
//	}


//	@Override
//	public Request getPreviousApprovedRequest(final int headId, final String trackingNumberStr) {
//		return getPreviousRequestByStatus(headId, trackingNumberStr, RequestStatus.APPROVED);
//	}


//	@Override
//	public RequestWorkflow getPreviousApprovedWorkflowByType(final int headId, final String trackingNumberStr, final int workflowTypeId) {
//		initializeRequestHash();
//		
//		List<Request> result = new ArrayList<Request>();
//		
//		for (Request req : listAll()) {
//			if( req.getId() == headId || req.getHeadId() == headId ) {
//				if( trackingNumberStr != null && req.getTrackingNumber() != null && req.getTrackingNumber().trim().toLowerCase().compareTo( trackingNumberStr.trim().toLowerCase() ) < 0 ) {
//					result.add(req);
//				}
//			}
//		}
//
//		if( result != null && result.size() > 0 ) {
//			Collections.sort( result, Request.getDescTrackingNumberComparator() );	//sort, most recent tracking number first
//			
//			for( Request prevReq : result ) {
//				try {
//
//					Set<RequestWorkflow> prevWorkflowSet = prevReq.getWorkflows(true);
//					if( prevWorkflowSet != null ) {
//						for( RequestWorkflow workflow : prevWorkflowSet ) {
//							if( workflow != null ) {
//
//								WorkflowTemplate template = workflow.getWorkflowTemplate();
//								if( template != null && template.getWorkflowTypeId() == workflowTypeId ) {	//workflow of the specified type
//
//									//if( workflow.getRequestStatus().getId() == RequestStatus.APPROVED.getId() ) {
//									if( workflow.isApproved() ) {	//this workflow is approved
//										return workflow;
//									}//end if
//
//								}//end if
//
//							}//end if
//						}//end for
//					}//end if
//					
//				} catch (Exception e) {
//					e.printStackTrace();
//					return null;
//				}
//			}//end for
//		}//end if
//
//		return null;
//	}


//	@Override
//	public Request getRequestWithPreviousApprovedWorkflow(final int headId, final String trackingNumberStr, final int workflowTypeId) {
//		initializeRequestHash();
//		
//		List<Request> result = new ArrayList<Request>();
//		
//		for (Request req : listAll()) {
//			if( req.getId() == headId || req.getHeadId() == headId ) {
//				if( trackingNumberStr != null && req.getTrackingNumber() != null && req.getTrackingNumber().trim().toLowerCase().compareTo( trackingNumberStr.trim().toLowerCase() ) < 0 ) {
//					result.add(req);
//				}
//			}
//		}
//
//		if( result != null && result.size() > 0 ) {
//			Collections.sort( result, Request.getDescTrackingNumberComparator() );	//sort, most recent tracking number first
//			
//			for( Request prevReq : result ) {
//				try {
//
//					Set<RequestWorkflow> prevWorkflowSet = prevReq.getWorkflows(true);
//					if( prevWorkflowSet != null && prevWorkflowSet.size() > 0 ) {
//						for( RequestWorkflow workflow : prevWorkflowSet ) {
//							if( workflow != null ) {
//
//								WorkflowTemplate template = workflow.getWorkflowTemplate();
//								if( template != null && template.getWorkflowTypeId() == workflowTypeId ) {	//workflow of the specified type
//
//									//if( workflow.getRequestStatus().getId() == RequestStatus.APPROVED.getId() ) {
//									if( workflow.isApproved() ) {	//this workflow is approved
//										return prevReq;
//									}//end if
//
//								}//end if
//
//							}//end if
//						}//end for
//
//					} else {
//
//						if( prevReq.getStatus().getId() == RequestStatus.APPROVED.getId() ) {	//top-level request was approved
//							return prevReq;
//						}//end if
//
//					}
//					
//				} catch (Exception e) {
//					e.printStackTrace();
//					return null;
//				}
//			}//end for
//		}//end if
//
//		return null;
//	}

	
	@Override
	public Request findByTrackingNumber(final String trackingNumberStr) throws ObjectNotFoundException {
		initializeRequestHash();
		
		if( trackingNumberStr == null || trackingNumberStr.trim().isEmpty() )
			return null;
		
		for (Request req : listAll()) {
			if( req.getTrackingNumber() != null && req.getTrackingNumber().trim().equalsIgnoreCase( trackingNumberStr.trim() ) ) {
				return req;
			}
		}

		throw new ObjectNotFoundException("No Request found with trackingNumber " + trackingNumberStr);	//failed to find this tracking number
	}
	

	private void initializeRequestHash() {
		if (initialized == true) {
			return;
		}
		
		initialized = true;
	}

	@Override
	public List<Request> findExpectedIRBSubmissionByDate(Date date) {
		// TODO Auto-generated method stub
		return null;
	}
	
}
