package gov.va.vss.web;

import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Locale;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.MessageSource;
import org.springframework.core.env.Environment;
import org.springframework.ui.ModelMap;
import org.springframework.web.servlet.support.RequestContextUtils;

import gov.va.shared.model.Permission.PermissionType;
import gov.va.shared.model.lookup.sds.VAFacility;
import gov.va.shared.persistence.AppUserDAO;
import gov.va.shared.persistence.AppUserFacilityDAO;
import gov.va.shared.persistence.lookup.sds.GenderDAO;
import gov.va.shared.persistence.lookup.sds.StateDAO;
import gov.va.shared.persistence.lookup.sds.VAFacilityDAO;
import gov.va.shared.service.AppUserService;
import gov.va.shared.service.VAFacilityService;
import gov.va.shared.util.SecurityUtil;
import gov.va.shared.util.ServletUtil;
import gov.va.shared.util.context.SessionUtil;
import gov.va.vss.model.facility.Facility;
import gov.va.vss.persistence.dao.AdjustedHoursEntryDAO;
import gov.va.vss.persistence.dao.ApplicationParametersDAO;
import gov.va.vss.persistence.dao.AwardCodeDAO;
import gov.va.vss.persistence.dao.AwardDAO;
import gov.va.vss.persistence.dao.DonationDetailDAO;
import gov.va.vss.persistence.dao.DonationLogDAO;
import gov.va.vss.persistence.dao.DonationReferenceDAO;
import gov.va.vss.persistence.dao.DonationSummaryDAO;
import gov.va.vss.persistence.dao.DonorDAO;
import gov.va.vss.persistence.dao.ExpenditureDAO;
import gov.va.vss.persistence.dao.LedgerAdjustmentDAO;
import gov.va.vss.persistence.dao.LetterTemplateDAO;
import gov.va.vss.persistence.dao.MealTicketDAO;
import gov.va.vss.persistence.dao.NationalOfficialDAO;
import gov.va.vss.persistence.dao.NotificationDAO;
import gov.va.vss.persistence.dao.OccasionalWorkEntryDAO;
import gov.va.vss.persistence.dao.ParkingStickerDAO;
import gov.va.vss.persistence.dao.UniformDAO;
import gov.va.vss.persistence.dao.WorkEntryDAO;
import gov.va.vss.persistence.dao.benefitingService.BenefitingServiceDAO;
import gov.va.vss.persistence.dao.benefitingService.BenefitingServiceRoleDAO;
import gov.va.vss.persistence.dao.benefitingService.BenefitingServiceRoleTemplateDAO;
import gov.va.vss.persistence.dao.benefitingService.BenefitingServiceRoleTypeDAO;
import gov.va.vss.persistence.dao.benefitingService.BenefitingServiceTemplateDAO;
import gov.va.vss.persistence.dao.donGenPostFund.DonGenPostFundDAO;
import gov.va.vss.persistence.dao.facility.FacilityDAO;
import gov.va.vss.persistence.dao.facility.KioskDAO;
import gov.va.vss.persistence.dao.facility.LocationDAO;
import gov.va.vss.persistence.dao.leie.ExcludedEntityDAO;
import gov.va.vss.persistence.dao.lookup.AdministrativeUnitDAO;
import gov.va.vss.persistence.dao.lookup.DonationTypeDAO;
import gov.va.vss.persistence.dao.lookup.DonorTypeDAO;
import gov.va.vss.persistence.dao.lookup.FacilityTypeDAO;
import gov.va.vss.persistence.dao.lookup.LanguageDAO;
import gov.va.vss.persistence.dao.lookup.NACStatusDAO;
import gov.va.vss.persistence.dao.lookup.OrganizationTypeDAO;
import gov.va.vss.persistence.dao.lookup.RequirementDateTypeDAO;
import gov.va.vss.persistence.dao.lookup.RequirementStatusDAO;
import gov.va.vss.persistence.dao.lookup.StaffTitleDAO;
import gov.va.vss.persistence.dao.lookup.StdCreditCardTypeDAO;
import gov.va.vss.persistence.dao.lookup.StdVAVSTitleDAO;
import gov.va.vss.persistence.dao.lookup.TransportationMethodDAO;
import gov.va.vss.persistence.dao.lookup.VoluntaryServiceTypeDAO;
import gov.va.vss.persistence.dao.lookup.VolunteerStatusDAO;
import gov.va.vss.persistence.dao.organization.OrganizationDAO;
import gov.va.vss.persistence.dao.requirement.RequirementDAO;
import gov.va.vss.persistence.dao.requirement.VolunteerRequirementDAO;
import gov.va.vss.persistence.dao.views.CombinedFacilityDAO;
import gov.va.vss.persistence.dao.volunteer.VoluntaryServiceStaffDAO;
import gov.va.vss.persistence.dao.volunteer.VolunteerAssignmentDAO;
import gov.va.vss.persistence.dao.volunteer.VolunteerDAO;
import gov.va.vss.persistence.dao.volunteer.VolunteerHistoryEntryDAO;
import gov.va.vss.persistence.dao.volunteer.VolunteerOrganizationDAO;
import gov.va.vss.persistence.dao.volunteer.demographics.VolDemoDAO;
import gov.va.vss.service.AdjustedHoursEntryService;
import gov.va.vss.service.AwardCodeService;
import gov.va.vss.service.AwardService;
import gov.va.vss.service.BenefitingServiceRoleService;
import gov.va.vss.service.BenefitingServiceRoleTemplateService;
import gov.va.vss.service.BenefitingServiceService;
import gov.va.vss.service.BenefitingServiceTemplateService;
import gov.va.vss.service.DonGenPostFundService;
import gov.va.vss.service.DonationLogService;
import gov.va.vss.service.DonationReferenceService;
import gov.va.vss.service.DonationService;
import gov.va.vss.service.DonorService;
import gov.va.vss.service.ExcludedEntityService;
import gov.va.vss.service.ExpenditureService;
import gov.va.vss.service.FacilityService;
import gov.va.vss.service.KioskService;
import gov.va.vss.service.LedgerAdjustmentService;
import gov.va.vss.service.LetterTemplateService;
import gov.va.vss.service.LocationService;
import gov.va.vss.service.MealTicketService;
import gov.va.vss.service.NationalOfficialService;
import gov.va.vss.service.NotificationService;
import gov.va.vss.service.OrganizationService;
import gov.va.vss.service.ParkingStickerService;
import gov.va.vss.service.RequirementService;
import gov.va.vss.service.ServiceParametersService;
import gov.va.vss.service.StaffTitleService;
import gov.va.vss.service.UniformService;
import gov.va.vss.service.VelocityService;
import gov.va.vss.service.VoluntaryServiceStaffService;
import gov.va.vss.service.email.EmailService;
import gov.va.vss.service.requirement.VolunteerRequirementService;
import gov.va.vss.service.volunteer.VolunteerService;
import gov.va.vss.service.workEntry.OccasionalWorkEntryService;
import gov.va.vss.service.workEntry.WorkEntryService;
import gov.va.vss.util.DateUtil;
import gov.va.vss.web.ajax.AjaxRequestHandler;
import gov.va.vss.web.validation.WebValidationService;

public abstract class AbstractCommonAppController {
	private static final Logger log = LoggerFactory.getLogger(AbstractCommonAppController.class);

	// ----------------------------------------- Static Fields and Constants

	public static final String DEFAULT_COMMAND_NAME = "command";

	public static final String FORM_READ_ONLY = "FORM_READ_ONLY";

	// ----------------------------------------- Fields

	// -------------------------- DAOs

	@Autowired
	protected AdjustedHoursEntryDAO adjustedHoursEntryDAO;
	@Autowired
	protected AdministrativeUnitDAO administrativeUnitDAO;
	@Autowired
	protected ApplicationParametersDAO applicationParameterDAO;
	@Autowired
	protected AppUserFacilityDAO appUserFacilityDAO;
	@Autowired
	protected AppUserDAO appUserDAO;
	@Autowired
	protected AwardDAO awardDAO;
	@Autowired
	protected AwardCodeDAO awardCodeDAO;
	@Autowired
	protected BenefitingServiceDAO benefitingServiceDAO;
	@Autowired
	protected BenefitingServiceRoleDAO benefitingServiceRoleDAO;
	@Autowired
	protected BenefitingServiceRoleTypeDAO benefitingServiceRoleTypeDAO;
	@Autowired
	protected BenefitingServiceTemplateDAO benefitingServiceTemplateDAO;
	@Autowired
	protected BenefitingServiceRoleTemplateDAO benefitingServiceRoleTemplateDAO;
	@Autowired
	protected CombinedFacilityDAO combinedInstitutionDAO;
	@Autowired
	protected DonationDetailDAO donationDetailDAO;
	@Autowired
	protected DonationLogDAO donationLogDAO;
	@Autowired
	protected DonationLogService donationLogService;
	@Autowired
	protected DonationSummaryDAO donationSummaryDAO;
	@Autowired
	protected DonationReferenceDAO donationReferenceDAO;
	@Autowired
	protected DonationTypeDAO donationTypeDAO;
	@Autowired
	protected DonGenPostFundDAO donGenPostFundDAO;
	@Autowired
	protected DonorDAO donorDAO;
	@Autowired
	protected DonorTypeDAO donorTypeDAO;
	@Autowired
	protected ExcludedEntityDAO excludedEntityDAO;
	@Autowired
	protected ExpenditureDAO expenditureDAO;
	@Autowired
	protected FacilityDAO facilityDAO;
	@Autowired
	protected FacilityTypeDAO facilityTypeDAO;
	@Autowired
	protected GenderDAO genderDAO;
	@Autowired
	protected KioskDAO kioskDAO;
	@Autowired
	protected LanguageDAO languageDAO;
	@Autowired
	protected LedgerAdjustmentDAO ledgerAdjustmentDAO;
	@Autowired
	protected LetterTemplateDAO letterTemplateDAO;
	@Autowired
	protected LocationDAO locationDAO;
	@Autowired
	protected MealTicketDAO mealTicketDAO;
	@Autowired
	protected NACStatusDAO nacStatusDAO;
	@Autowired
	protected NationalOfficialDAO nationalOfficialDAO;
	@Autowired
	protected NotificationDAO notificationDAO;
	@Autowired
	protected OccasionalWorkEntryDAO occasionalWorkEntryDAO;
	@Autowired
	protected OrganizationDAO organizationDAO;
	@Autowired
	protected OrganizationTypeDAO organizationTypeDAO;
	@Autowired
	protected ParkingStickerDAO parkingStickerDAO;
	@Autowired
	protected RequirementDAO requirementDAO;
	@Autowired
	protected RequirementStatusDAO requirementStatusDAO;
	@Autowired
	protected RequirementDateTypeDAO requirementDateTypeDAO;
	@Autowired
	protected StateDAO stateDAO;
	@Autowired
	protected StdCreditCardTypeDAO stdCreditCardTypeDAO;
	@Autowired
	protected StdVAVSTitleDAO stdVAVSTitleDAO;
	@Autowired
	protected StaffTitleDAO staffTitleDAO;
	@Autowired
	protected TransportationMethodDAO transportationMethodDAO;
	@Autowired
	protected UniformDAO uniformDAO;
	@Autowired
	protected VAFacilityDAO vaFacilityDAO;
	@Autowired
	protected VoluntaryServiceStaffDAO voluntaryServiceStaffDAO;
	@Autowired
	protected VoluntaryServiceTypeDAO voluntaryServiceTypeDAO;
	@Autowired
	protected VolunteerDAO volunteerDAO;
	@Autowired
	protected VolunteerAssignmentDAO volunteerAssignmentDAO;
	@Autowired
	protected VolDemoDAO volunteerDemographicsDAO;
	@Autowired
	protected VolunteerHistoryEntryDAO volunteerHistoryEntryDAO;
	@Autowired
	protected VolunteerOrganizationDAO volunteerOrganizationDAO;
	@Autowired
	protected VolunteerRequirementDAO volunteerRequirementDAO;
	@Autowired
	protected VolunteerStatusDAO volunteerStatusDAO;
	@Autowired
	protected WorkEntryDAO workEntryDAO;

	// -------------------------- Services

	@Autowired
	protected AwardService awardService;
	@Autowired
	protected AwardCodeService awardCodeService;
	@Autowired
	protected AdjustedHoursEntryService adjustedHoursEntryService;
	@Autowired
	protected AppUserService appUserService;
	@Autowired
	protected BenefitingServiceService benefitingServiceService;
	@Autowired
	protected BenefitingServiceTemplateService benefitingServiceTemplateService;
	@Autowired
	protected BenefitingServiceRoleService benefitingServiceRoleService;
	@Autowired
	protected BenefitingServiceRoleTemplateService benefitingServiceRoleTemplateService;
	@Autowired
	protected DonationService donationService;
	@Autowired
	protected DonationReferenceService donationReferenceService;
	@Autowired
	protected DonGenPostFundService donGenPostFundService;
	@Autowired
	protected DonorService donorService;
	@Autowired
	protected EmailService emailService;
	@Autowired
	protected ExcludedEntityService excludedEntityService;
	@Autowired
	protected ExpenditureService expenditureService;
	@Autowired
	protected FacilityService facilityService;
	@Autowired
	protected KioskService kioskService;
	@Autowired
	protected LedgerAdjustmentService ledgerAdjustmentService;
	@Autowired
	protected LetterTemplateService letterTemplateService;
	@Autowired
	protected LocationService locationService;
	@Autowired
	protected MealTicketService mealTicketService;
	@Autowired
	protected NationalOfficialService nationalOfficialService;
	@Autowired
	protected NotificationService notificationService;
	@Autowired
	protected OccasionalWorkEntryService occasionalWorkEntryService;
	@Autowired
	protected OrganizationService organizationService;
	@Autowired
	protected ParkingStickerService parkingStickerService;
	@Autowired
	protected RequirementService requirementService;
	@Autowired
	protected StaffTitleService staffTitleService;
	@Autowired
	protected VolunteerRequirementService volunteerRequirementService;
	@Autowired
	protected ServiceParametersService voluntaryServiceParametersService;
	@Autowired
	protected UniformService uniformService;
	@Autowired
	protected VAFacilityService vaFacilityService;
	@Autowired
	protected VelocityService velocityService;
	@Autowired
	protected VoluntaryServiceStaffService voluntaryServiceStaffService;
	@Autowired
	protected VolunteerService volunteerService;
	@Autowired
	protected WorkEntryService workEntryService;

	// -------------------------- Others

	@Autowired
	protected AjaxRequestHandler ajaxRequestHandler;
	@Autowired
	protected DateUtil dateUtil;
	@Autowired
	protected gov.va.vss.util.context.SessionUtil sessionUtil;
	@Autowired
	protected Environment env;
	@Autowired
	protected MessageSource messageSource;
	@Autowired
	protected WebValidationService webValidationService;

	@Autowired
	protected UserNotifier userNotifier;

	protected boolean isAjax(HttpServletRequest r) {
		return AjaxRequestHandler.isAjax(r);
	}

	protected String getMessage(String code) {
		return getMessage(code, new Object[] {});
	}

	protected String getMessage(String code, Object[] args) {
		return messageSource.getMessage(code, args, Locale.getDefault());
	}

	// ----------------------------------------- Utility Methods

	protected void requirePermissionsAtFacility(long vaFacilityId, PermissionType... requiredPermissionsAtStation) {
		if (ArrayUtils.isNotEmpty(requiredPermissionsAtStation)) {
			SecurityUtil.ensureAllPermissionsAtFacility(vaFacilityId, requiredPermissionsAtStation);
		}
	}

	protected void requirePermissionsAtCurrentFacility(PermissionType... requiredPermissionsAtFacility) {
		if (ArrayUtils.isNotEmpty(requiredPermissionsAtFacility)) {
			SecurityUtil.ensureAllPermissionsAtCurrentFacility(requiredPermissionsAtFacility);
		}
	}

	protected void setFormAsReadOnly(ModelMap model, boolean readOnly) {
		model.put(FORM_READ_ONLY, readOnly);
	}

	protected boolean formIsReadOnly(ModelMap model) {
		Boolean b = (Boolean) model.get(FORM_READ_ONLY);
		return b != null && b;
	}

	protected void setFormAsReadOnlyUnlessUserHasPermissions(ModelMap model, PermissionType... permissions) {
		boolean userHasPermissions = SecurityUtil.hasAllPermissionsAtCurrentFacility(permissions);
		setFormAsReadOnly(model, !userHasPermissions);
	}

	protected Locale getLocale(HttpServletRequest portletRequest) {
		HttpServletRequest request = ServletUtil.getThreadBoundServletRequest();
		Locale locale = request == null ? Locale.getDefault() : RequestContextUtils.getLocale(request);
		return locale;
	}

	protected String getCurrentUserName() {
		return SecurityUtil.getCurrentUserName();
	}

	protected VAFacility getSiteContext() {
		return SessionUtil.getSiteContext();
	}

	protected VAFacility getRequiredSiteContext() {
		VAFacility siteContext = getSiteContext();
		if (siteContext == null)
			throw new IllegalArgumentException("Site context required in this controller but not found");
		return siteContext;
	}

	protected void setFacilityContext(Facility f) {
		sessionUtil.setFacilityContext(f.getVaFacility(), f);
	}

	protected Long getFacilityContextId() {
		Facility f = gov.va.vss.util.context.SessionUtil.getFacilityContext();
		return f == null ? null : f.getId();
	}

	protected Facility getFacilityContext() {
		Facility f = gov.va.vss.util.context.SessionUtil.getFacilityContext();
		if (f == null) {
			VAFacility siteContext = SessionUtil.getSiteContext();
			if (siteContext == null)
				return null;

			f = facilityDAO.findByVAFacility(siteContext.getId());
			if (f == null)
				throw new IllegalStateException(
						"No Facility is currently linked to the VA site " + siteContext.getStationNumber()
								+ "; please contact support to remedy this via the Facility Management screen.");
			setFacilityContext(f);
			return f;
		} else {
			// reattach
			f = facilityDAO.findRequiredByPrimaryKey(f.getId());
			return f;
		}
	}

	protected Facility getRequiredFacilityContext() {
		Facility f = getFacilityContext();
		if (f == null)
			throw new IllegalArgumentException("Required facility context was not found");
		return f;
	}

	protected LocalDate getTodayAtFacility() {
		return LocalDate.now(getFacilityTimeZone());
	}

	protected ZoneId getFacilityTimeZone() {
		Facility facilityContext = getFacilityContext();
		return facilityContext == null ? ZoneId.systemDefault() : facilityContext.getTimeZone();
	}

}
