package com.jasamedika.medifirst2000.interceptor;

import com.jasamedika.medifirst2000.constants.Constants;
import com.jasamedika.medifirst2000.dao.LoginUserDao;
import com.jasamedika.medifirst2000.dao.RuanganDao;
import com.jasamedika.medifirst2000.entities.*;
import com.jasamedika.medifirst2000.notification.MessagePublisher;
import com.jasamedika.medifirst2000.security.model.AppPermission;
import com.jasamedika.medifirst2000.security.service.TokenAuthenticationService;
import com.jasamedika.medifirst2000.service.*;
import com.jasamedika.medifirst2000.util.CommonUtil;
import com.jasamedika.medifirst2000.vo.NotifMessagingSchedulerVO;
import com.jasamedika.medifirst2000.vo.NotifMessagingVO;
import com.jasamedika.medifirst2000.vo.NotifikasiMessageObjekModulVO;
import com.jasamedika.medifirst2000.vo.ObjekModulAplikasiVO;
import org.apache.commons.lang3.ArrayUtils;
import org.json.JSONArray;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * Interceptor class for All annotation method controller @AppPermission
 * 
 * @author Roberto, direka ulang oleh Syamsu
 */
public class AppInterceptor implements HandlerInterceptor {

	private static final String[] PRINT_PATTERN = { "/lap_", "/lap-", "/lap", "/cetak_", "/cetak-", "/print_",
			"/print-" };

	private static final String[] ADD_PATTERN = { "/save_", "/save-", "/add_", "/add-", "/simpan_", "/simpan-" };

	private static final String[] UPDATE_PATTERN = { "/update_", "/update-", "/edit_", "/edit-", "/ubah_", "/ubah-" };

	private static final String[] DELETE_PATTERN = { "/delete_", "/delete-", "/hapus_", "/hapus-" };

	@Autowired
	private TokenAuthenticationService tokenAuthenticationService;

	@Autowired
	private LoginUserDao loginUserDao;

	@Autowired
	private MapObjekModulToKelompokUserService mapObjekModulToKelompokUserService;

	@Autowired
	private MapObjekModulToLoginUserService mapObjekModulToLoginUserService;

	@Autowired
	private ObjekModulAplikasiService<ObjekModulAplikasiVO> objekModulAplikasiService;

	@Autowired
	MessagePublisher<String, Object> messagePublisher;

	@Autowired
	NotifMessagingService<NotifMessagingVO> notifMessagingService;

	@Autowired
	NotifikasiMessageObjekModulService<NotifikasiMessageObjekModulVO> notifikasiMessageObjekModulService;

	@Autowired
	NotifMessagingSchedulerService<NotifMessagingSchedulerVO> notifMessagingSchedulerService;

	@Autowired
	RuanganDao ruanganDao;

	List<LoginUser> loginUser;

	String namaUser;

	private boolean contains(String source, String[] matchers) {
		if (CommonUtil.isNullOrEmpty(source) || ArrayUtils.isEmpty(matchers))
			return false;
		for (String matcher : matchers)
			if (source.contains(matcher))
				return true;
		return false;
	}

	private String filterUrlForm(String urlForm) {
		String[] hasil = urlForm.split("/");
		StringBuilder potongan = new StringBuilder();
		for (int i = 0; i < hasil.length && i < 3; i++)
			potongan.append(hasil[i]).append("/");
		return potongan.toString();
	}

	private boolean checkAuthSecure(HttpServletRequest request, HttpServletResponse response) throws Exception {
		Authentication authentication = tokenAuthenticationService.getAuthentication(request);
		namaUser = authentication.getName();
		loginUser = loginUserDao.findByNamaUser(namaUser);
		if (CommonUtil.isNullOrEmpty(loginUser)) {
			response.setHeader("RequireSupervisor", "false");
			response.setHeader(Constants.MessageInfo.ERROR_MESSAGE, "Empty login user");
			response.setStatus(HttpServletResponse.SC_FORBIDDEN);
			return false;
		}
		return true;
	}

	private int checkUserPermision(LoginUser user, ObjekModulAplikasiVO objekModulAplikasiVO, int action) {
		int result = -1;
		MapObjekModulToLoginUser modul = mapObjekModulToLoginUserService
				.findByObjekModulAplikasi(objekModulAplikasiVO.getId(), user.getId());
		if (modul != null) {
			result = 0;
			switch (action) {
			case AppPermission.ADD:
				if (modul.getSimpan())
					result = 1;
				break;
			case AppPermission.UPDATE:
				if (modul.getEdit())
					result = 1;
				break;
			case AppPermission.DELETE:
				if (modul.getHapus())
					result = 1;
				break;
			case AppPermission.PRINT:
				if (modul.getCetak())
					result = 1;
				break;
			}
		}
		return result;
	}

	private int checkKelompokUserPermision(LoginUser user, ObjekModulAplikasiVO objekModulAplikasiVO, int action) {
		KelompokUser role = user.getKelompokUser();
		int result = -2;
		if (role != null) {
			MapObjekModulToKelompokUser modul = mapObjekModulToKelompokUserService
					.findByObjekModulAplikasi(objekModulAplikasiVO.getId(), role.getId());
			result = -1;
			if (modul != null) {
				result = 0;
				switch (action) {
				case AppPermission.ADD:
					if (modul.getSimpan())
						result = 1;
					break;
				case AppPermission.UPDATE:
					if (modul.getEdit())
						result = 1;
					break;
				case AppPermission.DELETE:
					if (modul.getHapus())
						result = 1;
					break;
				case AppPermission.PRINT:
					if (modul.getCetak())
						result = 1;
					break;
				}
			}
		}
		return result;
	}

	private boolean checkPermission(HttpServletRequest request, HttpServletResponse response, String AlamatUrlForm,
			int action) throws Exception {
		List<ObjekModulAplikasiVO> objekModulAplikasi = objekModulAplikasiService
				.findByAlamatUrlForm(filterUrlForm(AlamatUrlForm));
		if (CommonUtil.isNullOrEmpty(objekModulAplikasi))
			return true;
		if (!checkAuthSecure(request, response))
			return false;
		int resultUser = checkUserPermision(loginUser.get(0), objekModulAplikasi.get(0), action);
		if (resultUser == 0) {
			response.setHeader("RequireSupervisor", "true");
			response.setHeader(Constants.MessageInfo.ERROR_MESSAGE, "Action requires supervisor");
			response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
			return false;
		} else if (resultUser == 1) {
			response.setHeader(Constants.MessageInfo.INFO_MESSAGE, "Supervise execution success");
			response.setStatus(HttpServletResponse.SC_OK);
			return true;
		}
		int resultKelompokUser = checkKelompokUserPermision(loginUser.get(0), objekModulAplikasi.get(0), action);
		if (resultKelompokUser == -2) {
			return true;
		} else if (resultKelompokUser == -1) {
			return true;
		} else if (resultKelompokUser == 0) {
			response.setHeader("RequireSupervisor", "true");
			response.setHeader(Constants.MessageInfo.ERROR_MESSAGE, "Action requires supervisor");
			response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
			return false;
		} else {
			response.setHeader(Constants.MessageInfo.INFO_MESSAGE, "Supervise execution success");
			response.setStatus(HttpServletResponse.SC_OK);
			return true;
		}
	}

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
		HandlerMethod hm;
		Method method;
		response.setHeader("Access-Control-Expose-Headers", "RequireSupervisor" + ","
				+ Constants.MessageInfo.ERROR_MESSAGE + "," + Constants.MessageInfo.INFO_MESSAGE);
		String AlamatUrlForm = request.getHeader(Constants.HttpHeader.URL_FORM) == null ? ""
				: request.getHeader(Constants.HttpHeader.URL_FORM);
		String KdRuangan = request.getHeader(Constants.HttpHeader.KD_RUANGAN) == null ? ""
				: request.getHeader(Constants.HttpHeader.KD_RUANGAN);
		int action = AppPermission.VIEW;
		if (CommonUtil.isNullOrEmpty(AlamatUrlForm))
			return true;
		if (CommonUtil.isNullOrEmpty(KdRuangan) || "0".equals(KdRuangan))
			return true;
		String URI = request.getRequestURI();
		String methodHttp = request.getMethod();
		try {
			if (handler instanceof org.springframework.web.method.HandlerMethod) {
				hm = (HandlerMethod) handler;
				method = hm.getMethod();
			} else {
				return checkAuthSecure(request, response);
			}
			boolean usingAnno = method != null && method.isAnnotationPresent(AppPermission.class);
			boolean postM = "POST".equals(methodHttp);
			boolean signInOut = !URI.contains("auth/sign-in") && !URI.contains("auth/sign-out");
			boolean getM = "GET".equals(methodHttp);
			boolean cetak = contains(URI, PRINT_PATTERN);
			boolean simpan = contains(URI, ADD_PATTERN);
			boolean ubah = contains(URI, UPDATE_PATTERN);
			boolean hapus = contains(URI, DELETE_PATTERN);
			if (usingAnno) {
				if (method.getAnnotation(AppPermission.class) != null)
					action = method.getAnnotation(AppPermission.class).value();
				if (AppPermission.SPECIALS == action)
					return true;
			}
			if (getM && cetak) {
				action = AppPermission.PRINT;
				if (method != null && method.getAnnotation(AppPermission.class) != null)
					action = method.getAnnotation(AppPermission.class).value();
				return checkPermission(request, response, AlamatUrlForm, action);
			}
			if ((usingAnno || postM) && signInOut) {
				if (simpan) {
					action = AppPermission.ADD;
				} else if (ubah) {
					action = AppPermission.UPDATE;
				} else if (hapus) {
					action = AppPermission.DELETE;
				} else if (cetak) {
					action = AppPermission.PRINT;
				}
				if (method != null && method.getAnnotation(AppPermission.class) != null)
					action = method.getAnnotation(AppPermission.class).value();
				return checkPermission(request, response, AlamatUrlForm, action);
			}
		} catch (Exception e) {
			response.setHeader(Constants.MessageInfo.ERROR_MESSAGE, "Something wrong happened");
			response.setHeader("RequireSupervisor", "false");
			response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
			return false;
		}
		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object obj, ModelAndView mav) {
	}

	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object obj, Exception ex)
			throws Exception {
		String AlamatUrlForm = request.getHeader(Constants.HttpHeader.URL_FORM) == null ? ""
				: request.getHeader(Constants.HttpHeader.URL_FORM);
		String KdRuangan = request.getHeader(Constants.HttpHeader.KD_RUANGAN) == null ? ""
				: request.getHeader(Constants.HttpHeader.KD_RUANGAN);
		String idRuanganTujuan = request.getHeader(Constants.HttpHeader.KD_RUANGAN_T) == null ? ""
				: request.getHeader(Constants.HttpHeader.KD_RUANGAN_T);
		String idRuanganTujuanA = request.getHeader(Constants.HttpHeader.KD_RUANGAN_A) == null ? ""
				: request.getHeader(Constants.HttpHeader.KD_RUANGAN_A);
		String tglKirim = request.getHeader(Constants.HttpHeader.TGL_KIRIM) == null ? ""
				: request.getHeader(Constants.HttpHeader.TGL_KIRIM);
		boolean notHttpOK = response.getStatus() >= HttpServletResponse.SC_BAD_REQUEST;
		boolean notLewatMenu = CommonUtil.isNullOrEmpty(AlamatUrlForm) || CommonUtil.isNullOrEmpty(KdRuangan);
		Integer ruanganAsalId;
		try {
			ruanganAsalId = Integer.parseInt(KdRuangan);
		} catch (NumberFormatException e) {
			ruanganAsalId = 0;
		}
		boolean adaError = CommonUtil.isNotNullOrEmpty(ex);
		if (notHttpOK || notLewatMenu || adaError)
			return;
		Authentication authentication = tokenAuthenticationService.getAuthentication(request);
		if (CommonUtil.isNullOrEmpty(authentication))
			return;
		namaUser = authentication.getName();
		loginUser = loginUserDao.findByNamaUser(namaUser);
		if (CommonUtil.isNullOrEmpty(loginUser))
			return;
		String potongan = filterUrlForm(AlamatUrlForm);
		List<ObjekModulAplikasiVO> objekModulAplikasiVOs = objekModulAplikasiService.findByAlamatUrlForm(potongan);
		if (CommonUtil.isNullOrEmpty(objekModulAplikasiVOs))
			return;
		String URI = request.getRequestURI();
		Integer objekModulAplikasId = objekModulAplikasiVOs.get(0).getId();
		List<NotifMessagingVO> notifMessagingVOs = notifMessagingService
				.findByObjekModulAplikasiIdAndUrlBackEndOrUrlBackEndIsNull(objekModulAplikasId, URI);
		if (CommonUtil.isNullOrEmpty(notifMessagingVOs))
			return;
		Integer notifMessagingId = notifMessagingVOs.get(0).getId();
		if (CommonUtil.isNullOrEmpty(tglKirim)) {
			List<Integer> ruanganTujuansId = new ArrayList<>();
			if (!"".equals(idRuanganTujuan)) {
				JSONArray ruanganTujuanIds = new JSONArray(idRuanganTujuan);
				for (int i = 0; i < ruanganTujuanIds.length(); i++) {
					Integer ruanganTujuanId = ruanganTujuanIds.optInt(i, 0);
					ruanganTujuansId.add(ruanganTujuanId);
				}
			}
			if (!"".equals(idRuanganTujuanA) && ruanganTujuansId.isEmpty()) {
				JSONArray ruanganTujuanIds = new JSONArray(idRuanganTujuanA);
				for (int i = 0; i < ruanganTujuanIds.length(); i++) {
					Integer ruanganTujuanId = ruanganTujuanIds.optInt(i, 0);
					ruanganTujuansId.add(ruanganTujuanId);
				}
			}
			MessagePublisher.RabbitHole rabbitHole = messagePublisher.getRabbitHole();
			Pegawai pegawai = loginUser.get(0).getPegawai();
			Ruangan ruanganAsal = ruanganDao.findById(ruanganAsalId);
			if (!ruanganTujuansId.isEmpty())
				rabbitHole.sendNotif(rabbitHole, ruanganAsal, pegawai, notifikasiMessageObjekModulService,
						notifMessagingId, ruanganTujuansId);
		} else {
			JSONArray tglKirims = new JSONArray(tglKirim);
			JSONArray ruanganTujuanIds = new JSONArray(idRuanganTujuan);
			SimpleDateFormat myFormat = new SimpleDateFormat("yyyy-MM-dd");
			for (int i = 0; i < tglKirims.length(); i++) {
				String ctglKirim = tglKirims.optString(i, "");
				Integer cRuanganTujuanId = ruanganTujuanIds.optInt(i, 0);
				Date date = myFormat.parse(ctglKirim);
				NotifMessagingSchedulerVO vo = new NotifMessagingSchedulerVO();
				vo.setTglKirim(date);
				vo.setTerkirim(false);
				vo.setNotifMessagingId(notifMessagingId);
				vo.setRuanganIdAsal(ruanganAsalId);
				vo.setRuanganIdTujuan(cRuanganTujuanId);
				vo.setPegawaiId(loginUser.get(0).getPegawai().getId());
				vo.setKdProfile((short) 0);
				vo.setKodeExternal("");
				vo.setNamaExternal("");
				vo.setReportDisplay("");
				vo.setStatusEnabled(true);
				notifMessagingSchedulerService.add(vo);
			}
		}
	}
}
