package com.kospo.talk.config.filter;

import com.initech.eam.api.NXContext;
import com.initech.eam.api.NXUserAPI;
import com.initech.eam.nls.CookieManager;
import com.initech.eam.smartenforcer.SECode;
import com.kospo.talk.config.utils.CryptoUtil;
import com.kospo.talk.model.User;
import com.kospo.talk.repository.UserRepository;
import jakarta.servlet.FilterChain;
//import jakarta.servlet.ServletException;
//import jakarta.servlet.http.HttpServletRequest;
//import jakarta.servlet.http.HttpServletResponse;
//import jakarta.servlet.http.HttpSession;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import jakarta.servlet.ServletException;
import jakarta.servlet.http.Cookie;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;

import javax.security.sasl.AuthenticationException;

import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

import java.io.IOException;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;

@Slf4j
public class CustomBasicAuthenticationFilter extends BasicAuthenticationFilter {
    private UserRepository userRepository;

    private static final String SERVICE_NAME = "kdems";

    /**
     * ep url : 남부발전 POTAL SITE
     */
    private static final String EP_URL = "http://ep.kospo.co.kr/irj/portal";

    /**
     * domain_name : 남부발전
     */
    private static final String DOMAIN_NAME = SERVICE_NAME + ".kospo.co.kr";
    /**
     * domain_port : 남부발전 포트
     */
    private static final String DOMAIN_PORT = "80";

    /**
     * server url : 해당 Application의 URL --> 업무시스템에 맞게 URL 수정!
     */
    private static final String SERVER_URL = "http://" + DOMAIN_NAME;

    /**
     * ASCP(로그인후 리다이렉트 페이지) --> 설치한 경로에 맞게 수정!
     */
    private static final String ASCP_URL = SERVER_URL + ":" + DOMAIN_PORT + "/";

    private static final String custom_url = "";

    /**
     * nls url
     */
    private static final String NLS_URL = "http://sso.kospo.co.kr";
    /**
     * nls port
     */
    private static final String NLS_PORT = "8011";

    /**
     * 로그인을 위해서 NLS로 redirect할 URL (fail-over는 L4)
     */
    private static final String NLS_LOGIN_URL = NLS_URL + ":" + NLS_PORT + "/nls3/clientLogin.jsp";

    private String NLS_LOGOUT_URL = NLS_URL + ":" + NLS_PORT + "/nls3/NCLogout.jsp";

    /**
     * 에러 페이지 URL
     */
    private static final String NLS_ERROR_URL = NLS_URL + ":" + NLS_PORT + "/nls3/error.jsp";

    /**
     * Nexess API가 사용할 Nexess Daemon의 주소
     */
    private static final String ND_URL = "http://sso.kospo.co.kr:5480";
    private static final String ND_URL1 = "http://sso.kospo.co.kr:5480";


    private static Vector PROVIDER_LIST = new Vector();

    private static final int COOKIE_SESSTION_TIME_OUT = 3000000;

    /**
     * 인증 타입 (ID/PW 방식 : 1, 인증서 방식 : 3)
     */
    private static final String TOA = "1";
    /**
     * domain (.mento.com) --> 해당 Domain 맞게 수정!
     */
    private static final String SSO_DOMAIN = ".kospo.co.kr";

    private static final int timeout = 15000;
    private static NXContext context = null;


    public CustomBasicAuthenticationFilter(AuthenticationManager authenticationManager, UserRepository userRepository) {
        super(authenticationManager);
        this.userRepository = userRepository;
    }


      static {
          //PropertyConfigurator.configureAndWatch("D:/INISafeNexess/site/4.1.0/src/Web/WebContent/WEB-INF/logger.properties");
          List<String> serverurlList = new ArrayList<>();
          serverurlList.add(ND_URL1);
          //serverurlList.add(ND_URL2);

          context = new NXContext(serverurlList, timeout);
          CookieManager.setEncStatus(true);
//		CookieManager.setSameSiteStatus(false);

          //7-1. 테스트 시 sso로 테스트 할 경우
//        PROVIDER_LIST.add("sso.kospo.co.kr");

          //7-2. 운영에 배포할 때

          PROVIDER_LIST.add("sso1.kospo.co.kr");
          PROVIDER_LIST.add("sso2.kospo.co.kr");


          //NLS3 web.xml의 CookiePadding 값과 같아야 한다. 안그럼 검증 페일남
          //InitechEamUID +"_V42" .... 형태로 쿠명 생성됨
          SECode.setCookiePadding("_V42");
      }

    public NXContext getContext() {
        NXContext context = null;
        try {
            List serverurlList = new ArrayList();
            serverurlList.add(ND_URL1);
            context = new NXContext(serverurlList);
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return context;
    }

    public String getSsoId(jakarta.servlet.http.HttpServletRequest request) {

        String sso_id = getCookieValue(SECode.USER_ID, request);
        return sso_id;
    }

    public static String getCookieValue(String cookieName, jakarta.servlet.http.HttpServletRequest request) {
        try {
            Cookie[] cookies = request.getCookies();
            if (cookies == null) {
                return null;
            } else {
                String cookieValue = null;

                for(int i = 0; i < cookies.length; ++i) {
                    Cookie cookieInfo = cookies[i];
                    if (cookieInfo.getName().equals(cookieName)) {

                        if (!CookieManager.getEncStatus() || !cookieName.equals(SECode.USER_ID) && !cookieName.equals(SECode.USER_IP) && !cookieName.equals(SECode.USER_PID)) {
                            cookieValue = cookieInfo.getValue();
                            if (cookieValue != null) {
                                cookieValue = URLDecoder.decode(cookieValue);
                            }
                        } else {
                            cookieValue = CookieManager.decryptWithSEED(URLDecoder.decode(cookieInfo.getValue()));
                        }

                        return cookieValue;
                    }
                }

                return cookieValue;
            }
        } catch (Exception e) {
            log.error("", e);
            return "";
        }
    }

    public boolean AppAduditLog(HttpServletRequest request, String retCode) throws Exception {

        boolean ret = false;
        String sso_id = getSsoId(request);
        String sso_ip = null;
        String sso_mac = null;
        String sso_toa = null;
        String retValue = null;

        NXContext context = null;
        NXUserAPI nx_userapi = null;

        try {

            if (context == null)
                context = getContext();

            nx_userapi = new NXUserAPI(context);
            sso_id = CookieManager.encryptWithSEED(sso_id);
            sso_ip = CookieManager.getCookieValue("InitechEamClientip", request);
            sso_toa = CookieManager.getCookieValue(SECode.USER_TOA, request);
            sso_mac = CookieManager.getCookieValue("InitechEamClientmac", request);
            System.out.println("MAC" + sso_mac);

            System.out.println(sso_ip);
            if (!retCode.equals("0"))
                retValue = "1";
            else
                retValue = "0";

            ret = nx_userapi.setAccessHistory(SERVICE_NAME, sso_mac, sso_ip, sso_id, sso_toa, retValue, retCode);

            if (ret)
                ret = true;

        } catch (Exception e) {

            log.error(e.getMessage());
            return ret;

        }

        return ret;
    }

    // 통합인증 세션을 체크 하기 위하여 사용되는 API
    public String getEamSessionCheck(HttpServletRequest request, HttpServletResponse response) {
        String retCode = "";
        try {
            retCode = CookieManager.verifyNexessCookie(request, response, 10, COOKIE_SESSTION_TIME_OUT, PROVIDER_LIST);
        } catch (Exception npe) {
            log.error(npe.getMessage());
        }
        return retCode;
    }

    private Boolean getEamSessionCheckIP(HttpServletRequest request) throws Exception {
        String clientIP = request.getHeader("X-Forwarded-For");
        if (clientIP == null || clientIP.length() < 1 || "unknown".equalsIgnoreCase(clientIP)) {
            clientIP = request.getRemoteAddr();
        }
        String sso_uip = CookieManager.getCookieValue(SECode.USER_IP, request);
        log.debug("*================== clientIP: " + clientIP + ", ssoUIP: " + sso_uip);
        if (!(clientIP.equals(sso_uip))) {
            return false;
        }
        return true;
    }

    public String getEamSessionCheckAndAgentValid(HttpServletRequest request, HttpServletResponse response) {
        String retCode = "";
        try {
            Boolean result = getEamSessionCheckIP(request);
            if (result == true) {
                retCode = CookieManager.verifyNexessCookieAndAgentVaild(request, response, 10,
                        COOKIE_SESSTION_TIME_OUT, PROVIDER_LIST, SERVER_URL, context);
            } else {
                //쿠키값 전체를 탈취해 로그인 시도한 경우
                retCode = "1004";
            }
        } catch (Exception npe) {
            log.error(npe.getMessage());
            return "5451";
        }
        return retCode;
    }


    public void goErrorPage(HttpServletResponse response, int error_code) throws Exception {
        CookieManager.removeNexessCookie(SSO_DOMAIN, response);
        CookieManager.addCookie(SECode.USER_URL, ASCP_URL, SSO_DOMAIN, response);
        response.sendRedirect(NLS_ERROR_URL + "?errorCode=" + error_code);
    }

    public void goLoginPage(HttpServletResponse response) throws Exception {
        CookieManager.addCookie(SECode.USER_URL, ASCP_URL, SSO_DOMAIN, response);
        CookieManager.addCookie(SECode.R_TOA, TOA, SSO_DOMAIN, response);

        //자체 로그인을 할경우 로그인 페이지 Setting
        if (custom_url.equals("")) {
            CookieManager.addCookie("CLP", "", SSO_DOMAIN, response);
        } else {
            CookieManager.addCookie("CLP", custom_url, SSO_DOMAIN, response);
        }

        //    response.sendRedirect(NLS_LOGIN_URL);
    }



    @Override
    protected void doFilterInternal(jakarta.servlet.http.HttpServletRequest request, jakarta.servlet.http.HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
//        log.info("★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★" + request.getRequestURI());
//        System.out.println("★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★" + request.getRequestURI());

        jakarta.servlet.http.HttpSession session = request.getSession();
        User user = (User) session.getAttribute("user");

        String sso_sabun = "";
        boolean isSso = true;

        try {
            if (sso_sabun == null || sso_sabun.isEmpty()) {
                sso_sabun = request.getHeader("Authentication");

                if(sso_sabun != null) sso_sabun = CryptoUtil.decrypt(sso_sabun);
//                System.out.println("header : " + sso_sabun);
            }
        } catch(Exception e) {
            new RuntimeException(e);
        }

        try {
            if (sso_sabun == null || sso_sabun.isEmpty()) {
                sso_sabun = request.getParameter("Authentication");
                if(sso_sabun != null) sso_sabun = CryptoUtil.decrypt(sso_sabun);
//                System.out.println("request : " + sso_sabun);
            }
        } catch(Exception e) {
            new RuntimeException(e);
        }


        isSso = sso_sabun == null || sso_sabun.equals("");

        if(!isSso) user = null;

        /* SSO 인증 부분 */
        if (user == null) {
            /*sso 인증  개발시 header 정보에 사번 추가 하여 테스트*/

            if(isSso) {
                sso_sabun = getSsoId(request);
//                System.out.println("sso : " + sso_sabun);
            }

            user = userRepository.findBySabun(sso_sabun);
            if(user != null) {
                Authentication authentication = null;
                try {
                    if (user.getAuthorities() == null) {
                        authentication = new UsernamePasswordAuthenticationToken(user, null, Arrays.asList(new SimpleGrantedAuthority("ROLE_DEFAULT")));
                    } else {
                        authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
                    }
                } catch(Exception e) {
                    authentication = new UsernamePasswordAuthenticationToken(user, null, Arrays.asList(new SimpleGrantedAuthority("ROLE_DEFAULT")));
                }
                SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
                securityContext.setAuthentication(authentication);
                SecurityContextHolder.setContext(securityContext);
                SecurityContextHolder.getContext().getAuthentication();
                session.setAttribute("user", user);
            }

        } else {
            Authentication getAuth = SecurityContextHolder.getContext().getAuthentication();
            if (getAuth == null) {
                Authentication authentication = null;
                try {
                    if (user.getAuthorities() == null) {
                        authentication = new UsernamePasswordAuthenticationToken(user, null, Arrays.asList(new SimpleGrantedAuthority("ROLE_DEFAULT")));
                    } else {
                        authentication = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
                    }
                } catch(Exception e) {
                    authentication = new UsernamePasswordAuthenticationToken(user, null, Arrays.asList(new SimpleGrantedAuthority("ROLE_DEFAULT")));
                }
                SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
                securityContext.setAuthentication(authentication);
                SecurityContextHolder.setContext(securityContext);
                SecurityContextHolder.getContext().getAuthentication();
            }
        }

        chain.doFilter(request, response);
        /*추후 적용 필요 할수도 잇음*/
//        SecurityContextHolder.clearContext();
    }

    public CustomBasicAuthenticationFilter(AuthenticationManager authenticationManager) {
        super(authenticationManager);
    }

    public CustomBasicAuthenticationFilter(AuthenticationManager authenticationManager, AuthenticationEntryPoint authenticationEntryPoint) {
        super(authenticationManager, authenticationEntryPoint);
    }
}

