package com.kingdee.eas.custom.integration.weaver.util;

import com.kingdee.eas.cp.eip.sso.ltpa.LtpaTokenManager;
import com.kingdee.eas.custom.integration.base.exception.CommonErrorCodes;
import com.kingdee.eas.custom.integration.base.exception.IntegrationException;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.util.HtmlUtils;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

/**
 * OAuth 成功后用 HTML 表单 POST 进 index2sso。
 * 新 base 仍用 Location 带 LTPA，Apusic 上容易多重编码；泛微链路在 weaver 包内自行落地。
 */
public final class WeaverShrLanding {

    private static final Logger log = LoggerFactory.getLogger(WeaverShrLanding.class);

    private WeaverShrLanding() {
    }

    public static void writePostForm(HttpServletResponse response,
                                     String shrServerName,
                                     String userNumber,
                                     String redirectUrl) throws IOException {
        if (StringUtils.isAnyBlank(shrServerName, userNumber, redirectUrl)) {
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "单点参数不完整");
        }
        try {
            String password = LtpaTokenManager.generate(userNumber, LtpaTokenManager.getDefaultLtpaConfig()).toString();
            String actionUrl = shrServerName.replaceAll("/+$", "") + "/shr/index2sso.jsp";
            // index2sso 会把 redirectTo 原样拼进 login.do 查询串且不再 encode；
            // 表单先 encode 一次，getParameter 后仍保留 %26，避免 &inFrame 等被截断。
            // 与产品 OTP2sso/OSF2sso 对 redirectTo 双重编码同一思路。
            String redirectForForm = URLEncoder.encode(redirectUrl, StandardCharsets.UTF_8.name());
            log.info("weaver SHR POST index2sso, username={}, action={}, redirectRaw={}",
                    userNumber, actionUrl, redirectUrl);

            try {
                response.resetBuffer();
            } catch (IllegalStateException ignored) {
            }
            response.setStatus(HttpServletResponse.SC_OK);
            response.setContentType("text/html;charset=UTF-8");
            response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
            PrintWriter out = response.getWriter();
            out.write("<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><title>SSO</title></head><body>");
            out.write("<form id=\"ssoForm\" method=\"post\" target=\"_top\" action=\"");
            out.write(HtmlUtils.htmlEscape(actionUrl));
            out.write("\">");
            out.write("<input type=\"hidden\" name=\"username\" value=\"");
            out.write(HtmlUtils.htmlEscape(userNumber));
            out.write("\"/>");
            out.write("<input type=\"hidden\" name=\"password\" value=\"");
            out.write(HtmlUtils.htmlEscape(password));
            out.write("\"/>");
            out.write("<input type=\"hidden\" name=\"redirectTo\" value=\"");
            out.write(HtmlUtils.htmlEscape(redirectForForm));
            out.write("\"/></form>");
            out.write("<script type=\"text/javascript\">document.getElementById(\"ssoForm\").submit();</script>");
            out.write("</body></html>");
            out.flush();
        } catch (IntegrationException e) {
            throw e;
        } catch (Exception e) {
            log.error("写 SHR 单点表单失败: {}", e.getMessage(), e);
            throw new IntegrationException(CommonErrorCodes.BUILD_SSO_SHR_URL_FAILED, "写SHR单点表单失败");
        }
    }

    /**
     * 直接写 Location，避免 Apusic sendRedirect 对已编码 URL 再编码。
     */
    public static void redirectLocation(HttpServletResponse response, String location) throws IOException {
        if (response.isCommitted()) {
            throw new IllegalStateException("response already committed");
        }
        try {
            response.resetBuffer();
        } catch (IllegalStateException ignored) {
        }
        response.setStatus(HttpServletResponse.SC_FOUND);
        response.setHeader("Location", location);
    }

    /**
     * 联邦退出：输出小页，由浏览器 encodeURIComponent(service) 再跳泛微 logout。
     * 避免 Java 侧对 service 预编码后再进 Location 导致泛微白名单校验失败。
     */
    public static void writeLogoutThenService(HttpServletResponse response,
                                              String weaverLogoutBase,
                                              String serviceUrl) throws IOException {
        if (StringUtils.isAnyBlank(weaverLogoutBase, serviceUrl)) {
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "logout params incomplete");
        }
        try {
            response.resetBuffer();
        } catch (IllegalStateException ignored) {
        }
        response.setStatus(HttpServletResponse.SC_OK);
        response.setContentType("text/html;charset=UTF-8");
        response.setHeader("Cache-Control", "no-store, no-cache, must-revalidate");
        String logoutBaseJs = toJsSingleQuoted(weaverLogoutBase);
        String serviceJs = toJsSingleQuoted(serviceUrl);
        PrintWriter out = response.getWriter();
        out.write("<!DOCTYPE html><html><head><meta charset=\"UTF-8\"><title>Logout</title></head><body>");
        out.write("<script type=\"text/javascript\">");
        out.write("(function(){");
        out.write("var logoutBase='");
        out.write(logoutBaseJs);
        out.write("';");
        out.write("var serviceUrl='");
        out.write(serviceJs);
        out.write("';");
        out.write("var join=logoutBase.indexOf('?')>=0?'&':'?';");
        out.write("var target=logoutBase+join+'service='+encodeURIComponent(serviceUrl);");
        out.write("window.location.replace(target);");
        out.write("})();");
        out.write("</script></body></html>");
        out.flush();
        log.info("weaver logout html, logoutBase={}, service={}", weaverLogoutBase, serviceUrl);
    }

    private static String toJsSingleQuoted(String raw) {
        if (raw == null) {
            return "";
        }
        return raw.replace("\\", "\\\\").replace("'", "\\'").replace("\r", "").replace("\n", "");
    }
}
