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

import com.kingdee.eas.base.permission.UserInfo;
import com.kingdee.eas.custom.integration.base.annotation.ApiDoc;
import com.kingdee.eas.custom.integration.base.annotation.SkipAuth;
import com.kingdee.eas.custom.integration.base.annotation.SkipCallLog;
import com.kingdee.eas.custom.integration.base.bizEnum.DeviceType;
import com.kingdee.eas.custom.integration.base.bizEnum.RedirectTypeEnum;
import com.kingdee.eas.custom.integration.base.config.AppConfig;
import com.kingdee.eas.custom.integration.base.config.CommonConfig;
import com.kingdee.eas.custom.integration.base.config.OAuthConfig;
import com.kingdee.eas.custom.integration.base.controller.AbstractAuthController;
import com.kingdee.eas.custom.integration.base.exception.CommonErrorCodes;
import com.kingdee.eas.custom.integration.base.exception.IntegrationException;
import com.kingdee.eas.custom.integration.base.service.IntegrationConfigService;
import com.kingdee.eas.custom.integration.base.service.OAuthService;
import com.kingdee.eas.custom.integration.base.service.StateValidator;
import com.kingdee.eas.custom.integration.base.service.detection.PlatformDetectionService;
import com.kingdee.eas.custom.integration.base.util.SHRApiUtils;
import com.kingdee.eas.custom.integration.weaver.config.WeaverConfigService;
import com.kingdee.eas.custom.integration.weaver.util.WeaverApiUtils;
import com.kingdee.eas.custom.integration.weaver.util.WeaverShrLanding;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;

/**
 * 泛微 E10 OAuth 入口，继承 base 的 AbstractAuthController。
 * PC/待办深链请走 /auth/weaver/app(.do)，不要依赖 UnifiedAuthController 的 platform UA 识别。
 * Apusic 需同时映射 *.do。
 */
@SkipAuth
@SkipCallLog
@Controller
@RequestMapping("/auth")
public class WeaverAuthController extends AbstractAuthController {

    private final IntegrationConfigService integrationConfigService;
    private final StateValidator weaverStateValidator;
    private final WeaverConfigService weaverConfigService;
    public WeaverAuthController(IntegrationConfigService integrationConfigService,
                                OAuthService oAuthService,
                                StateValidator stateValidator,
                                PlatformDetectionService detectionService,
                                WeaverConfigService weaverConfigService) {
        super(integrationConfigService, oAuthService, stateValidator, detectionService);
        this.integrationConfigService = integrationConfigService;
        this.weaverStateValidator = stateValidator;
        this.weaverConfigService = weaverConfigService;
    }

    @Override
    protected String platform() {
        return "weaver";
    }

    /**
     * 门户/待办显式入口：用 appId 生成 state 后跳转 OAuth。
     * 不要用统一 /auth/app?platform=weaver（那条走 base 的 UnifiedAuthController）。
     */
    @GetMapping({"/weaver/app", "/weaver/app.do"})
    @ApiDoc(name = "泛微显式appId入口", desc = "按 appId 生成 state 后进入 OAuth2 授权")
    public void app(@RequestParam String appId,
                    @RequestParam String redirect,
                    HttpServletResponse response) throws IOException {
        if (StringUtils.isBlank(appId) || StringUtils.isBlank(redirect)) {
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "appId/redirect required");
        }
        AppConfig config = integrationConfigService.getByAppId(appId);
        if (config == null || !"weaver".equalsIgnoreCase(config.getPlatform())) {
            log.warn("weaver appId invalid: {}", appId);
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "config_not_found");
        }
        String state = weaverStateValidator.generate(redirect, config.getAppType());
        log.info("weaver app entry, appId={}, appType={}, redirect={}",
                appId, config.getAppType(), redirect);
        doAuth(state, response);
    }

    @GetMapping({"/weaver", "/weaver.do"})
    @ApiDoc(name = "泛微授权入口", desc = "已有 state 时直接进入 OAuth2 授权")
    public void auth(@RequestParam(name = "state", required = false) String state,
                     HttpServletResponse response) throws IOException {
        if (StringUtils.isBlank(state)) {
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "Invalid state");
        }
        doAuth(state, response);
    }

    @GetMapping({"/callback/weaver/pa", "/callback/weaver/pa.do"})
    @ApiDoc(name = "泛微OAuth回调(pa)", desc = "主应用 pa 回调，兼容 .do")
    public void callbackPa(@RequestParam(name = "code", required = false) String code,
                           @RequestParam(name = "state", required = false) String state,
                           HttpServletRequest request,
                           HttpServletResponse response) throws IOException {
        log.info("weaver OAuth callback, appType=pa, uri={}", request.getRequestURI());
        if (StringUtils.isBlank(code)) {
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "code is required");
        }
        doWeaverCallback(code, state, "pa", request, response);
    }

    @GetMapping({"/callback/weaver/{appType}", "/callback/weaver/{appType}.do"})
    @ApiDoc(name = "泛微OAuth回调", desc = "按路径 appType 回调，兼容 .do")
    public void callback(@RequestParam(name = "code", required = false) String code,
                         @RequestParam(name = "state", required = false) String state,
                         @PathVariable String appType,
                         HttpServletRequest request,
                         HttpServletResponse response) throws IOException {
        log.info("weaver OAuth callback, appType={}, uri={}", appType, request.getRequestURI());
        if (StringUtils.isBlank(code)) {
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "code is required");
        }
        doWeaverCallback(code, state, appType, request, response);
    }

    /**
     * 联邦退出：把 logout 的 service 指回本系统 SSO 入口再进 authorize。
     * 这样退出后再进 SHR 会先到 E10 登录页，而不是带着旧会话直接回 SHR。
     * 传 appId+redirect 时拼出 SSO 入口；也可直接传 service。
     * service 域名须在泛微退出重定向白名单内。
     */
    @GetMapping({"/weaver/logout", "/weaver/logout.do"})
    @ApiDoc(name = "泛微联邦退出", desc = "跳转泛微 logout，service 指向 SHR SSO 入口")
    public void logout(@RequestParam(name = "appId", required = false) String appId,
                       @RequestParam(name = "redirect", required = false) String redirect,
                       @RequestParam(name = "service", required = false) String service,
                       HttpServletResponse response) throws IOException {
        // Federated logout: service = SHR SSO entry. Domain must be on Weaver logout whitelist,
        // otherwise Weaver rewrites to ecology home and user stays in Weaver after login.
        String weaverLogoutService = service;
        if (StringUtils.isNotBlank(appId) && StringUtils.isNotBlank(redirect)) {
            AppConfig config = integrationConfigService.getByAppId(appId);
            if (config == null || !"weaver".equalsIgnoreCase(config.getPlatform())) {
                throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "config_not_found");
            }
            OAuthConfig oauthConfig = integrationConfigService.getOauthConfig();
            if (!oauthConfig.isRedirectAllowed(redirect)) {
                throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "Redirect not allowed");
            }
            CommonConfig commonConfig = integrationConfigService.getCommonConfig();
            String shrBase = commonConfig.getShrServerName();
            if (StringUtils.isBlank(shrBase)) {
                throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "shrServerName missing");
            }
            weaverLogoutService = shrBase.replaceAll("/+$", "")
                    + "/qy/auth/weaver/app.do?appId=" + URLEncoder.encode(appId, "UTF-8")
                    + "&redirect=" + URLEncoder.encode(redirect, "UTF-8");
            log.info("weaver federated logout, appId={}, redirect={}, ssoEntryAsService={}",
                    appId, redirect, weaverLogoutService);
        }
        if (StringUtils.isBlank(weaverLogoutService)) {
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "appId/redirect or service required");
        }
        String logoutBase = weaverConfigService.getWeaverConfig().getLogoutUrl();
        log.info("redirect to weaver logout, logoutBase={}, service={}", logoutBase, weaverLogoutService);
        WeaverShrLanding.writeLogoutThenService(response, logoutBase, weaverLogoutService);
    }


    @Override
    protected void doAuth(String state, HttpServletResponse response) throws IOException {
        String redirect = weaverStateValidator.extractRedirect(state);
        String appType = weaverStateValidator.extractAppType(state);
        if (StringUtils.isBlank(appType)) {
            appType = "pa";
        }
        if (StringUtils.isBlank(redirect)) {
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "Invalid state");
        }
        AppConfig config = integrationConfigService.get(platform(), appType);
        if (config == null) {
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "config_not_found");
        }
        String oauthUrl = oAuthService.buildAuthUrl(config, state);
        log.info("redirect to OAuth, platform={}, appType={}", platform(), appType);
        WeaverShrLanding.redirectLocation(response, oauthUrl);
    }

    /**
     * 回调成功后：桌面进 sHR 用 POST 表单，避免 Location 带 LTPA。
     * pathAppType 在 state 无 appType 时兜底。
     */
    private void doWeaverCallback(String code,
                                  String state,
                                  String pathAppType,
                                  HttpServletRequest request,
                                  HttpServletResponse response) throws IOException {
        String redirect = weaverStateValidator.extractRedirect(state);
        String appType = weaverStateValidator.extractAppType(state);
        if (StringUtils.isBlank(appType) && StringUtils.isNotBlank(pathAppType)) {
            appType = pathAppType.trim();
        }
        if (StringUtils.isBlank(appType)) {
            appType = "pa";
        }
        if (StringUtils.isBlank(redirect)) {
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "state invalid");
        }

        OAuthConfig oauthConfig = integrationConfigService.getOauthConfig();
        if (!oauthConfig.isRedirectAllowed(redirect)) {
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "Redirect not allowed");
        }

        AppConfig config = integrationConfigService.get(platform(), appType);
        if (config == null) {
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "config_not_found");
        }

        String userAccount;
        try {
            userAccount = oAuthService.getUserId(config, code);
        } catch (IntegrationException e) {
            throw e;
        } catch (Exception e) {
            log.error("getUserId failed, platform={}, error={}", platform(), e.getMessage(), e);
            throw new IntegrationException(CommonErrorCodes.SYSTEM_ERROR, e.getMessage(), e);
        }

        UserInfo userInfo = SHRApiUtils.getUserInfo(config.getUserMappingField(), userAccount);
        SHRApiUtils.checkBillExist(redirect);

        String ua = request.getHeader("User-Agent");
        DeviceType device = detectionService.detectDevice(ua);
        if (device == null) {
            device = DeviceType.DESKTOP;
        }

        Boolean isRedirectMbos = RedirectTypeEnum.TO_MBOS.equals(config.getMobileRedirectType());
        Boolean isDesktopToShr = RedirectTypeEnum.TO_SHR.equals(config.getDesktopRedirectType());

        if ((device == DeviceType.MOBILE && isRedirectMbos)
                || (device == DeviceType.DESKTOP && !isDesktopToShr)) {
            String mBosEid = config.getMBosEid();
            if (StringUtils.isBlank(mBosEid) || mBosEid.contains("请填写")) {
                throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "MBosEid 未配置");
            }
            redirect += "&eid=" + mBosEid;
            String finalRedirect = SHRApiUtils.generateToMBosUrl(userInfo.getNumber(), redirect);
            log.info("auth success(mbos), platform={}, user={}, redirect={}",
                    platform(), userAccount, finalRedirect);
            WeaverShrLanding.redirectLocation(response, finalRedirect);
        } else {
            CommonConfig commonConfig = integrationConfigService.getCommonConfig();
            log.info("auth success(shr), platform={}, user={}, redirect={}",
                    platform(), userAccount, redirect);
            WeaverShrLanding.writePostForm(
                    response, commonConfig.getShrServerName(), userInfo.getNumber(), redirect);
        }
    }
}
