package com.samsung.interceptor;

import com.samsung.accesslog.AccessLogService;
import com.samsung.accesslog.entity.SysUseLog;
import com.samsung.accesslog.enums.ReqType;
import com.samsung.authentication.Account;
import com.samsung.batch.enums.LogFlag;
import com.samsung.common.IdGenService;
import com.samsung.common.util.WebUtil;
import com.samsung.menu.MenuService;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.log4j.Log4j2;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.UriTemplate;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Log4j2
public class LoggingInterceptor implements HandlerInterceptor {

    private static final String DATE_PATTERN = "yyyyMMdd HHmmssSSS";

    @Autowired
    private AccessLogService accessLogService;
    @Autowired
    private MenuService menuService;
    @Autowired
    private IdGenService idGenService;
    @Autowired
    private WebUtil webUtil;

    @Value("${access-log.store-type}")
    private String storeType;

    @Value("${node-id}")
    private String node;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        long start = System.nanoTime();
        request.setAttribute("start", start);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) {

    	String requestMethod = request.getMethod();
        if(HttpMethod.OPTIONS.name().equals(requestMethod)) return;

        String requestURI = request.getRequestURI().substring(request.getContextPath().length());
        String getDecodedRequestURI = webUtil.getDecodedRequestUrl(requestURI);
        String query = request.getQueryString();
        String menuId = request.getHeader("menu-Id");
        String pageId = request.getHeader("page-id");
        String originalUserId = request.getHeader("original-user-id");

        SimpleDateFormat formatter = new SimpleDateFormat(DATE_PATTERN, Locale.getDefault());
        String sTime = formatter.format(new Date());

        long start = (long) request.getAttribute("start");
        long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);

        String userId = "anonymous";
        if (ObjectUtils.isNotEmpty(Account.currentUser())) userId = Account.currentUser().getUserId();

        SysUseLog sysUseLog = new SysUseLog();

        sysUseLog.setLogOccurId(idGenService.getNextStringId());

        // 메뉴에 등록된 API 호출 시 API 전체 경로를 저장.
        if(menuId != null) sysUseLog.setDescription(checkApiUrl(menuId, requestMethod, getDecodedRequestURI));

        sysUseLog.setNodeId(node);
        sysUseLog.setUserId(userId);
        sysUseLog.setUseFromDate(sTime.substring(0, 8));
        sysUseLog.setUseFromHhmmss(sTime.substring(9, 15));
        sysUseLog.setUseThruDatetime(new Date());
        sysUseLog.setResponseTime(elapsedTime);
        sysUseLog.setPath(getDecodedRequestURI);

        if (query != null) {
            try {
                query = URLDecoder.decode(query, "UTF-8");
            } catch (UnsupportedEncodingException e) {
                log.error(e.getMessage(), e);
            }
            String queryStr = StringUtils.replace(query, "'", "\\'");
            sysUseLog.setParameter(queryStr);
            sysUseLog.setUrl(requestURI + "?" + queryStr);
        }

        sysUseLog.setUseIp(webUtil.getClientIp(request));
        sysUseLog.setBrowserTypeName(request.getHeader("User-Agent"));
        sysUseLog.setLogFlag(LogFlag.ACCESS_LOG.getValue());
        sysUseLog.setReqType(ReqType.WEB);
        sysUseLog.setFirstRegDatetime(new Date());
        sysUseLog.setMenuId(menuId);
        sysUseLog.setPageId(pageId);
        sysUseLog.setMethod(requestMethod);

        // 대리 로그인 시 별도 이력 남김
        if(StringUtils.isNotEmpty(originalUserId)){
            sysUseLog.setLogFlag(LogFlag.IMPERSONATION_ACCESS.getValue());
            sysUseLog.setDescription("Original User Id : " + originalUserId);
        }

        try {
            if ("db".equalsIgnoreCase(storeType)) {
                accessLogService.insertAccessLog(sysUseLog);
            } else {
                accessLogService.logAccess(sysUseLog);
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    private String checkApiUrl(String menuId, String requestMethod, String requestUri) {

        UriTemplate uriTemplate;
        List<Map<String, String>> apiListByMenuId = new ArrayList<>();

        if(menuService.getApiFullPathList().containsKey(menuId)) {
            apiListByMenuId = menuService.getApiFullPathList().get(menuId);
        }

        for(Map<String, String> api : apiListByMenuId) {
            uriTemplate = new UriTemplate(api.get("apiPath"));
            if(uriTemplate.matches(requestUri) && requestMethod.equals(api.get("httpMethod"))) return api.get("fullPath");
        }
        return null;
    }
}
