package com.red.circle.auth.endpoint;

import com.red.circle.auth.common.LoginAccessValidationService;
import com.red.circle.auth.common.ProcessToken;
import com.red.circle.auth.dto.TokenCredentialCO;
import com.red.circle.auth.response.AuthErrorCode;
import com.red.circle.auth.storage.RedCircleCredentialService;
import com.red.circle.component.redis.service.RedisService;
import com.red.circle.framework.core.asserts.ResponseAssert;
import com.red.circle.framework.core.dto.CommonCommand;
import com.red.circle.framework.core.request.RequestClientEnum;
import com.red.circle.framework.core.security.UserCredential;
import com.red.circle.framework.web.spring.ApplicationRequestUtils;
import com.red.circle.other.inner.asserts.user.UserErrorCode;
import com.red.circle.other.inner.endpoint.sys.SysCountryCodeClient;
import com.red.circle.other.inner.endpoint.user.user.AppUserAccountClient;
import com.red.circle.other.inner.enums.user.AuthTypeEnum;
import com.red.circle.other.inner.model.cmd.user.UserChannelCredentialCmd;
import com.red.circle.other.inner.model.cmd.user.account.AccountLoginCmd;
import com.red.circle.other.inner.model.cmd.user.account.CreateAccountCmd;
import com.red.circle.other.inner.model.cmd.user.account.MobileCredentialCmd;
import com.red.circle.other.inner.model.dto.sys.SysCountryCodeDTO;
import com.red.circle.other.inner.model.dto.user.account.UserAccountDTO;
import com.red.circle.tool.core.text.StringUtils;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import static com.red.circle.tool.core.text.StringPool.SYMBOL_COMMA;

/**
 * 令牌端点.
 *
 * @author pengliang on 2023/5/5
 * @eo.api-type http
 * @eo.groupName 认证服务
 * @eo.path /auth
 */
@RestController
public class EndpointRestController {

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

    private final LoginAccessValidationService loginAccessValidationService;
    private final ProcessToken processToken;
    private final AppUserAccountClient appUserAccountClient;
    private final RedCircleCredentialService redCircleCredentialService;
    private final SysCountryCodeClient sysCountryCodeService;
    private final RedisService redisService;

    public EndpointRestController(LoginAccessValidationService loginAccessValidationService,
                                  ProcessToken processToken,
                                  AppUserAccountClient appUserAccountClient,
                                  RedCircleCredentialService redCircleCredentialService,
                                  SysCountryCodeClient sysCountryCodeService,
                                  RedisService redisService) {
        this.loginAccessValidationService = loginAccessValidationService;
        this.processToken = processToken;
        this.appUserAccountClient = appUserAccountClient;
        this.redCircleCredentialService = redCircleCredentialService;
        this.sysCountryCodeService = sysCountryCodeService;
        this.redisService = redisService;
    }

    /**
     * 检查token.
     *
     * @eo.hidden
     */
    @GetMapping("/token/check")
    public UserCredential check(HttpServletRequest request) {

        String authorization = ApplicationRequestUtils.getReqToken(request);
        ResponseAssert.notBlank(AuthErrorCode.AUTH_UNAUTHORIZED, authorization);

        // 设备是否正常(封禁设备会移除令牌，这一步可以不做)

        // IP是否被封禁
        UserCredential userCredential = UserCredential.parseToken(authorization);

        // 比对凭据
        UserCredential loginCredential = redCircleCredentialService.getByUserId(
                userCredential.getUserId());

        ResponseAssert.notNull(AuthErrorCode.AUTH_UNAUTHORIZED, loginCredential);
        ResponseAssert.isTrue(AuthErrorCode.AUTH_UNAUTHORIZED,
                Objects.equals(userCredential.getSign(), loginCredential.getSign()));
        return userCredential;
    }

    /**
     * 注销账号.
     *
     * @eo.name 注销账号.
     * @eo.url /logout
     * @eo.method delete
     * @eo.request-type formdata
     */
    @DeleteMapping("/logout")
    public void logout(CommonCommand cmd) {
        signOut(cmd);
        if (!Objects.equals(cmd.getReqClient(), RequestClientEnum.OPS)) {
            appUserAccountClient.logout(cmd);
        }
        // TODO 注销房间
        // roomProfileManagerService.delUserRoom(baseInfo.getId());
    }

    /**
     * 退出登录.
     *
     * @eo.name 退出登录.
     * @eo.url /sign-out
     * @eo.method delete
     * @eo.request-type formdata
     */
    @DeleteMapping("/sign-out")
    public void signOut(CommonCommand cmd) {
        redCircleCredentialService.removeByUserId(cmd.getReqUserId());
        appUserAccountClient.signOut(cmd);
    }

    /**
     * App用户注册.
     *
     * @eo.name App用户注册.
     * @eo.url /account/create
     * @eo.method post
     * @eo.request-type json
     */
    @PostMapping("/account/create")
    public TokenCredentialCO createAccount(@RequestBody @Validated CreateAccountCmd cmd, HttpServletRequest request) {
        log.warn("cmd {}", cmd);
        if (cmd.getType().equalsIgnoreCase(AuthTypeEnum.IMEI.getKey())&& cmd.getReqClient().isAndroid()) {
            //设备禁止登录
            ResponseAssert.failure(AuthErrorCode.AUTH_DEVICE_NOT_SUPPORT);
        }
        UserAccountDTO account = ResponseAssert.requiredSuccess(appUserAccountClient.create(cmd));
        ResponseAssert.notNull(UserErrorCode.REGISTRATION_FAILED, account);
        TokenCredentialCO tokenCredentialCO = processToken.createUserCredential(account);
        return submitLoginAccessValidation(tokenCredentialCO, cmd.requireReqSysOrigin(), cmd.getReqZoneId(), request);
    }

    /**
     * App用户注册.
     *
     * @eo.name App用户注册.
     * @eo.url /account/create
     * @eo.method post
     * @eo.request-type json
     */
    @PostMapping("/account/create/getRegion")
    public SysCountryCodeDTO createAccount(HttpServletRequest request) {
        String ipAddr = getIpAddr(request);
        log.warn("cmd getRegion ip {}", ipAddr);
        //单个ip 每天最多6次
        String key = "getRegion:" + ipAddr;
        String value = redisService.getString(key);
        if (StringUtils.isNotBlank(value)) {
            int count = Integer.parseInt(value);
            if (count >= 6) {
                return null;
            }
            redisService.increment(key, 1);
        } else {
            redisService.increment(key, 1, 1, TimeUnit.DAYS);
        }
        String s = loginAccessValidationService.getCountryId(ipAddr);
        List<SysCountryCodeDTO> countryList = sysCountryCodeService.listOpenCountry().getBody();
        return countryList.stream()
                .filter(action -> action.getAlphaTwo().equals(s))
                .findFirst()
                .orElse(null);


    }

    /**
     * 手机号登录.
     *
     * @eo.name 手机号登录.
     * @eo.url /account/login/mobile
     * @eo.method post
     * @eo.request-type json
     */
    @PostMapping("/account/login/mobile")
    public TokenCredentialCO loginMobile(@RequestBody @Validated MobileCredentialCmd cmd, HttpServletRequest request) throws IOException {
        // 兼容iOS审核
        if (Objects.equals(cmd.getPhoneNumber(), "13684965043")) {
            cmd.setPhonePrefix(86);
        }
        TokenCredentialCO tokenCredentialCO = processToken.createUserCredential(ResponseAssert.requiredSuccess(
                appUserAccountClient.mobileCredential(cmd)));
        return submitLoginAccessValidation(tokenCredentialCO, cmd.requireReqSysOrigin(), cmd.getReqZoneId(), request);
    }

    /**
     * 渠道登录.
     *
     * @eo.name 渠道登录.
     * @eo.url /account/login/channel
     * @eo.method post
     * @eo.request-type json
     */
    @PostMapping("/account/login/channel")
    public TokenCredentialCO login(@RequestBody @Validated UserChannelCredentialCmd cmd, HttpServletRequest request) {
        TokenCredentialCO tokenCredentialCO = processToken.createUserCredential(ResponseAssert.requiredSuccess(
                appUserAccountClient.channelCredential(cmd)));
        return submitLoginAccessValidation(tokenCredentialCO, cmd.requireReqSysOrigin(), cmd.getReqZoneId(), request);
    }

    /**
     * 账号登录.
     */
    @PostMapping(value = "/account/login")
    public TokenCredentialCO accountLogin(@RequestBody @Validated AccountLoginCmd cmd, HttpServletRequest request) {
        TokenCredentialCO tokenCredentialCO = processToken.createUserCredential(
                ResponseAssert.requiredSuccess(appUserAccountClient.accountLogin(cmd)));
        return submitLoginAccessValidation(tokenCredentialCO, cmd.requireReqSysOrigin(), cmd.getReqZoneId(), request);
    }

    /**
     * 账号登录.
     */
    @PostMapping(value = "/account/losgin")
    public TokenCredentialCO accountLogin1(@RequestBody @Validated AccountLoginCmd cmd, HttpServletRequest request) {
        TokenCredentialCO tokenCredentialCO = processToken.createUserCredential(
                ResponseAssert.requiredSuccess(appUserAccountClient.accountLogin(cmd)));
        return submitLoginAccessValidation(tokenCredentialCO, cmd.requireReqSysOrigin(), cmd.getReqZoneId(), request);
    }

    private TokenCredentialCO submitLoginAccessValidation(
            TokenCredentialCO tokenCredentialCO,
            String reqSysOrigin,
            String reqZoneId,
            HttpServletRequest request
    ) {
        loginAccessValidationService.validateLoginAccessAsync(
                tokenCredentialCO.getUserCredential(),
                reqSysOrigin,
                reqZoneId,
                tokenCredentialCO.getUserProfile().getAccount(),
                getIpAddr(request)
        );
        return tokenCredentialCO;
    }

    public static String getIpAddr(HttpServletRequest request) {
        // nginx代理获取的真实用户ip
        String ip = request.getHeader("X-Real-IP");
        if (org.apache.commons.lang3.StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("X-Forwarded-For");
        }
        if (org.apache.commons.lang3.StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (org.apache.commons.lang3.StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (org.apache.commons.lang3.StringUtils.isBlank(ip) || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        /*
          对于通过多个代理的情况， 第一个IP为客户端真实IP,多个IP按照','分割 "***.***.***.***".length() =
          15
         */
        if (ip != null && ip.length() > 15) {
            if (ip.indexOf(SYMBOL_COMMA) > 0) {
                ip = ip.substring(0, ip.indexOf(","));
            }
        }
        return ip;
    }
}
