package incheon.com.security;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Arrays;

/**
 * 인증 실패 시 처리
 * - API 요청: 401 JSON 응답
 * - 레이어 팝업: 팝업 닫기 + 로그인 안내
 * - 일반 페이지: SSO 포털(운영) 또는 로컬 로그인(개발)으로 리다이렉트
 */
@Slf4j
@Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint {

    private final RequestMappingHandlerMapping handlerMapping;
    private final Environment environment;

    @Value("${sso.portal.url}")
    private String ssoPortalUrl;

    @Value("${sso.system.url}")
    private String ssoSystemUrl;

    public CustomAuthenticationEntryPoint(
            @Qualifier("requestMappingHandlerMapping") RequestMappingHandlerMapping handlerMapping,
            Environment environment) {
        this.handlerMapping = handlerMapping;
        this.environment = environment;
    }

    /**
     * 운영 프로필인지 확인
     */
    private boolean isProductionProfile() {
        String[] activeProfiles = environment.getActiveProfiles();
        return Arrays.stream(activeProfiles).anyMatch(p -> p.equalsIgnoreCase("prod"));
    }

    @Override
    public void commence(HttpServletRequest request, HttpServletResponse response,
                         AuthenticationException authException) throws IOException, ServletException {

        String requestUri = request.getRequestURI();
        boolean isApiRequest = isRestControllerRequest(request);
        boolean isLayerPopup = isLayerPopupRequest(request);

        HttpSession session = request.getSession(true);
        Integer ssoRedirectCount = (Integer) session.getAttribute("SSO_REDIRECT_COUNT");
        if (ssoRedirectCount == null) {
            ssoRedirectCount = 0;
        }

        log.debug("[SSO] 인증 필요 - uri: {}, isApi: {}", requestUri, isApiRequest);

        if (isApiRequest) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.setContentType("application/json;charset=UTF-8");
            response.getWriter().write("{\"code\":401,\"message\":\"인증이 필요합니다. 로그인 페이지로 이동합니다.\"}");

        } else if (isLayerPopup) {
            handleLayerPopupUnauthorized(request, response);

        } else {
            String targetUrl = request.getRequestURI();
            String queryString = request.getQueryString();
            if (queryString != null) {
                targetUrl += "?" + queryString;
            }

            if (isProductionProfile()) {
                // 무한 루프 방지
                if (ssoRedirectCount >= 1) {
                    log.warn("[SSO] 무한 루프 감지 - uri: {}", requestUri);
                    session.removeAttribute("SSO_REDIRECT_COUNT");
                    session.removeAttribute("SSO_ID");
                    response.sendRedirect("/magicsso/sso_login_required.jsp");
                    return;
                }
                session.setAttribute("SSO_REDIRECT_COUNT", ssoRedirectCount + 1);

                // SSO 포털로 리다이렉트
                String loginProcessUrl = "/sso/login-process.do?target=" + URLEncoder.encode(targetUrl, "UTF-8");
                String ssoRedirectUrl = ssoPortalUrl +
                    "?conUrl=" + URLEncoder.encode(ssoSystemUrl, "UTF-8") +
                    "&returnUrl=" + URLEncoder.encode(loginProcessUrl, "UTF-8");
                response.sendRedirect(ssoRedirectUrl);

            } else {
                // 로컬 로그인 페이지
                response.sendRedirect("/?returnUrl=" + URLEncoder.encode(targetUrl, "UTF-8") + "&error=unauthorized");
            }
        }
    }

    private String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Real-IP");
        }
        if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }
        return ip;
    }

    /**
     * @RestController 또는 @ResponseBody 여부 확인
     */
    private boolean isRestControllerRequest(HttpServletRequest request) {
        try {
            HandlerExecutionChain chain = handlerMapping.getHandler(request);
            if (chain != null && chain.getHandler() instanceof HandlerMethod) {
                HandlerMethod handlerMethod = (HandlerMethod) chain.getHandler();
                Class<?> beanType = handlerMethod.getBeanType();
                return beanType.isAnnotationPresent(RestController.class)
                    || handlerMethod.hasMethodAnnotation(ResponseBody.class);
            }
        } catch (Exception e) {
            return "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
        }
        return "XMLHttpRequest".equals(request.getHeader("X-Requested-With"));
    }

    /**
     * 레이어 팝업 요청 여부 (_layerPopup=true)
     */
    private boolean isLayerPopupRequest(HttpServletRequest request) {
        return "true".equalsIgnoreCase(request.getParameter("_layerPopup"));
    }

    /**
     * 레이어 팝업 인증 실패 처리 (팝업 닫기 + 로그인 안내)
     */
    private void handleLayerPopupUnauthorized(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String targetUrl = request.getRequestURI();
        String queryString = request.getQueryString();
        if (queryString != null) {
            targetUrl += "?" + queryString;
        }

        String loginUrl;
        if (isProductionProfile()) {
            String loginProcessUrl = "/sso/login-process.do?target=" + URLEncoder.encode(targetUrl, "UTF-8");
            loginUrl = ssoPortalUrl +
                "?conUrl=" + URLEncoder.encode(ssoSystemUrl, "UTF-8") +
                "&returnUrl=" + URLEncoder.encode(loginProcessUrl, "UTF-8");
        } else {
            loginUrl = "/?returnUrl=" + URLEncoder.encode(targetUrl, "UTF-8") + "&error=unauthorized";
        }

        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType("text/html;charset=UTF-8");

        StringBuilder html = new StringBuilder();
        html.append("<!-- LAYER_POPUP_AUTH_REQUIRED -->\n");
        html.append("<script type='text/javascript'>\n");
        html.append("(function() {\n");
        html.append("  if (typeof ICAPP !== 'undefined' && ICAPP.popup && ICAPP.popup.closeAll) {\n");
        html.append("    ICAPP.popup.closeAll();\n");
        html.append("  }\n");
        html.append("  if (typeof ICAPP !== 'undefined' && ICAPP.alert) {\n");
        html.append("    ICAPP.alert('세션이 만료되었습니다.\\n로그인 후 이용해주세요.', function() {\n");
        html.append("      window.location.href = '").append(loginUrl).append("';\n");
        html.append("    });\n");
        html.append("  } else {\n");
        html.append("    alert('세션이 만료되었습니다.\\n로그인 후 이용해주세요.');\n");
        html.append("    window.location.href = '").append(loginUrl).append("';\n");
        html.append("  }\n");
        html.append("})();\n");
        html.append("</script>\n");

        response.getWriter().write(html.toString());
    }
} 