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

import com.kingdee.eas.base.permission.UserInfo;
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.exception.CommonErrorCodes;
import com.kingdee.eas.custom.integration.base.exception.IntegrationException;
import com.kingdee.eas.custom.integration.base.service.AppConfigService;
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 org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;

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

/**
 * 平台认证抽象控制器，封装钉钉/飞书通用的 OAuth 认证流程。
 *
 * <p>子类只需实现 {@link #platform()} 返回平台标识（"dingtalk" / "feishu"）。
 * {@code appType} 通过 state 参数在 OAuth 全流程中传递，
 * 支持同一平台多个应用（如流程助手 "pa"、员工自助 "ess"）。</p>
 *
 * <p>state 格式：{@code Base64Url(redirect + "|" + appType)}</p>
 *
 * <p>流程：</p>
 * <ol>
 *   <li>sHR 生成待办链接，state 中携带 redirect 和 appType</li>
 *   <li>UnifiedAuthController 识别平台、提取 appType 后转发</li>
 *   <li>{@link #doAuth} 提取 redirect 和 appType，查配置，跳转 OAuth 授权页</li>
 *   <li>OAuth 回调到达，{@link #doCallback} 提取 redirect 和 appType，code 换 token，
 *       根据终端类型分流：移动端→BOS，桌面端→sHR</li>
 * </ol>
 */
public abstract class AbstractAuthController {
    protected final Logger log = org.slf4j.LoggerFactory.getLogger(getClass());

    protected final AppConfigService appConfigService;
    protected final OAuthService oAuthService;
    protected final StateValidator stateValidator;
    protected final PlatformDetectionService detectionService;

    protected AbstractAuthController(AppConfigService appConfigService,
                                     OAuthService oAuthService,
                                     StateValidator stateValidator,
                                     PlatformDetectionService detectionService) {
        this.appConfigService = appConfigService;
        this.oAuthService = oAuthService;
        this.stateValidator = stateValidator;
        this.detectionService = detectionService;
    }

    protected abstract String platform();

    protected void doAuth(String state, HttpServletResponse response) throws IOException {
        String redirect = stateValidator.extractRedirect(state);
        String appType = stateValidator.extractAppType(state);
        if (StringUtils.isBlank(redirect) || StringUtils.isBlank(appType)) {
            log.error("failed to extract redirect or appType from state, platform={}", platform());
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "Invalid state");
        }

        AppConfig config = appConfigService.get(platform(), appType);
        if (config == null) {
            log.error("app config not found, platform={}, appType={}", platform(), appType);
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "config_not_found");
        }

        String oauthUrl = oAuthService.buildAuthUrl(config, state);
        log.info("redirect to OAuth, platform={}, appType={}", platform(), appType);
        response.sendRedirect(oauthUrl);
    }

    /**
     * 处理 OAuth 回调。
     */
    protected void doCallback(String code,
                              String state,
                              HttpServletRequest request,
                              HttpServletResponse response) throws IOException {
        String redirect = stateValidator.extractRedirect(state);
        String appType = stateValidator.extractAppType(state);
        if (StringUtils.isBlank(redirect) || StringUtils.isBlank(appType)) {
            log.warn("state invalid, platform={}", platform());
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "state invalid");
        }

        if (!appConfigService.isRedirectAllowed(redirect)) {
            log.warn("redirect not allowed, redirect={}", redirect);
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "Redirect not allowed");
        }

        AppConfig config = appConfigService.get(platform(), appType);
        if (config == null) {
            log.error("app config not found in callback, platform={}, appType={}", platform(), appType);
            throw new IntegrationException(CommonErrorCodes.PARAM_MISSING, "config_not_found");
        }

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

        //3、检查sHR用户是否存在
        UserInfo userInfo = SHRApiUtils.getUserInfo(config.getUserMappingField(), userAccount);

        //4、校验单据是否存在
        SHRApiUtils.checkBillExist(redirect);

        //5、构建单点移动bos地址
        // 设备分流：移动端→BOS，桌面端→sHR
        String ua = request.getHeader("User-Agent");
        log.info("userAccount: {}", userAccount);
        
        DeviceType device = detectionService.detectDevice(ua);

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

        String finalRedirect;
        if ((device == DeviceType.MOBILE && isRedirectMbos) || (device == DeviceType.DESKTOP && !isDesktopToShr)) {
            //跳转移动bos
            String mBosEid = config.getMBosEid();
            redirect += "&eid=" + mBosEid;
            finalRedirect = SHRApiUtils.generateToMBosUrl(userInfo.getNumber(), redirect);
        } else {
            finalRedirect = SHRApiUtils.generateDirectLoginUrl(appConfigService.getShrServerName(), userInfo.getNumber(), redirect);
        }

        log.info("auth success, platform={}, appType={}, device={}, userAccount={}, redirect={}",
                platform(), appType, device, userAccount, finalRedirect);
        response.sendRedirect(finalRedirect);
    }
}
