package com.ruoyi.framework.web.service;

import com.ruoyi.cms.util.oauth.OauthClient;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.entity.tymh.authority.QxUserRole;
import com.ruoyi.common.core.domain.entity.tymh.nwToken.NwTokenResult;
import com.ruoyi.common.core.domain.entity.tymh.nwToken.NwUserInfoResult;
import com.ruoyi.common.core.domain.entity.tymh.nwToken.PortalTokenCacheDTO;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.MessageUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.ServletUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.framework.manager.factory.AsyncFactory;
import com.ruoyi.framework.security.context.AuthenticationContextHolder;
import com.ruoyi.system.service.ISysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

@Service
public class OauthLoginService {

    @Autowired
    private OauthClient oauthClient;

    @Autowired
    private TokenService tokenService;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private UserDetailsService userDetailsService;
    // Redis缓存：门户UserID → 若依本地用户名（避免重复匹配数据库）
    private static final String REDIS_KEY_PORTAL_USER_MAPPING = "portal:user:mapping:";
    // 门户 Token 存储前缀（Redis 键：门户 userId → 门户 Token 信息）
    private static final String REDIS_KEY_PORTAL_TOKEN = "portal:token:";

    /**
     * OAuth 登录流程：通过授权码获取系统令牌
     * @param code 前端传入的 OAuth 授权码
     * @return 系统内部令牌（供前端后续使用）
     */
    public String oauthLogin(String code) {
        try {
            //获取门户token
            NwTokenResult nwTokenResult = oauthClient.nwGetToken(code);
            if (!"0".equals(nwTokenResult.getErrflag())) {
                throw new ServiceException("获取门户 Token 失败：" + nwTokenResult.getErrtext());
            }
            String accessToken = nwTokenResult.getAccessToken();
            Long expiresIn = nwTokenResult.getExpiresIn();
            if (StringUtils.isEmpty(accessToken) || expiresIn == null || expiresIn <= 0) {
                throw new ServiceException("门户 Token 无效或有效期异常");
            }
            //获取门户userInfo
            NwUserInfoResult portalUser = oauthClient.nwGetUserInfo(accessToken);
            //匹配/创建本地用户
            String localUsername = getOrCreateLocalUser(portalUser);
            //执行原来的登录流程
            Authentication authentication = authenticateLocalUser(localUsername);
            AsyncManager.me().execute(AsyncFactory.recordLogininfor(localUsername, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
            LoginUser loginUser = (LoginUser) authentication.getPrincipal();

            storePortalToken(loginUser.getUserId(), nwTokenResult);
            recordLoginInfo(loginUser.getUserId());
            return tokenService.createToken(loginUser);
        } catch (IOException | TimeoutException e) {
            throw new ServiceException("OAuth 登录失败：" + e.getMessage());
        }
    }

    /**
     * 匹配/创建本地用户，返回若依本地用户名
     */
    private String getOrCreateLocalUser(NwUserInfoResult portalUser) {
        Long portalUserId = parsePortalUserId(portalUser.getUserid());

        // 先从Redis查询缓存的本地用户名
        String cacheKey = REDIS_KEY_PORTAL_USER_MAPPING + portalUserId;
        String localUsername = redisCache.getCacheObject(cacheKey);
        if (StringUtils.isNotBlank(localUsername)) {
            try {
                //更新用户信息
                updateUserInfo(portalUser);
            }catch (Exception e){
                e.printStackTrace();
            }
            return localUsername;
        }

        // 缓存未命中，查询本地用户
        SysUser localUser = sysUserService.selectUserById(portalUserId);

        if (localUser == null) {
            // 本地无用户，自动创建
            localUser = createLocalUser(portalUser, portalUserId);
            // 缓存门户UserID与本地用户名的映射（有效期1天，可调整）
            redisCache.setCacheObject(cacheKey, localUser.getUserName(), 1, TimeUnit.DAYS);
            return localUser.getUserName();
        }

        // 缓存映射关系（更新有效期）
        redisCache.setCacheObject(cacheKey, localUser.getUserName(), 1, TimeUnit.DAYS);
        return localUser.getUserName();
    }

    /**
     * 门户UserID字符串转Long
     */
    private Long parsePortalUserId(String portalUserIdStr) {
        try {
            return Long.parseLong(portalUserIdStr);
        } catch (NumberFormatException e) {
            throw new ServiceException("门户用户ID格式错误：" + portalUserIdStr);
        }
    }

    /**
     * 自动创建本地用户
     */
    private SysUser createLocalUser(NwUserInfoResult portalUser, Long portalUserId) {
        SysUser newUser = new SysUser();
        String localUsername = "portal_" + portalUserId;
        newUser.setUserName(localUsername);
        newUser.setNickName(portalUser.getName());
        newUser.setIdCard(portalUser.getIdcardno());
        newUser.setUserId(portalUserId);
        newUser.setPassword(SecurityUtils.encryptPassword("123456"));
        newUser.setDelFlag("0");
        try {
            //查询权限，保存权限
            List<QxUserRole> userRoleList=oauthClient.getUserRoleList(portalUserId);
            if(userRoleList!=null&&userRoleList.size()>0){
                Long[] longs=userRoleList.stream().mapToLong(QxUserRole::getRoleId).boxed().toArray(Long[]::new);;
                newUser.setRoleIds(longs);
            }else {
                throw new Exception("未查询到用户角色，请授权后访问!");
            }
            // 调用若依原生方法新增用户（自动处理角色关联，需提前配置默认角色）
            sysUserService.insertUser(newUser);
        }catch (Exception e){
            e.printStackTrace();
        }
        return newUser;
    }

    /**
     * 复用若依认证机制，触发 UserDetailsService 加载 LoginUser
     * （关键：用本地用户名构建认证令牌，无需密码，因为门户已完成身份校验）
     */
    private Authentication authenticateLocalUser(String localUsername) {
        Authentication authentication = null;
        try {
            UserDetails userDetails = userDetailsService.loadUserByUsername(localUsername);

            authentication = new UsernamePasswordAuthenticationToken(
                    userDetails,
                    null,  // 密码为 null，彻底绕过 Spring Security 的密码校验
                    userDetails.getAuthorities()
            );

            AuthenticationContextHolder.setContext(authentication);
        } catch (Exception e) {
            throw new ServiceException("本地用户认证失败：" + e.getMessage());
        } finally {
            AuthenticationContextHolder.clearContext();
        }
        return authentication;
    }

    /**
     * 存储门户 Token 到 Redis（结构化存储，含过期时间）
     */
    private void storePortalToken(Long portalUserId, NwTokenResult tokenResult) {
        // 构建 Redis 键（门户 userId 作为唯一标识）
        String redisKey = REDIS_KEY_PORTAL_TOKEN + portalUserId;

        // 封装 Token 信息（含 accessToken、过期时间戳）
        PortalTokenCacheDTO tokenCache = new PortalTokenCacheDTO();
        tokenCache.setAccessToken(tokenResult.getAccessToken());
        tokenCache.setExpireTimestamp(System.currentTimeMillis() + tokenResult.getExpiresIn() * 1000);

        redisCache.setCacheObject(redisKey, tokenCache, safeLongToInt(tokenResult.getExpiresIn()), TimeUnit.SECONDS);
    }

    /**
     * 修改用户信息
     * @param portalUser
     */
    private void updateUserInfo(NwUserInfoResult portalUser){
        SysUser sysUser=new SysUser();
        Long portalUserId=parsePortalUserId(portalUser.getUserid());
        //查询用户角色
        try {
            List<QxUserRole> userRoleList=oauthClient.getUserRoleList(portalUserId);
            if(userRoleList!=null&&userRoleList.size()>0){
                Long[] longs=userRoleList.stream().mapToLong(QxUserRole::getRoleId).boxed().toArray(Long[]::new);;
                sysUser.setRoleIds(longs);
            }else {
                throw new Exception("未查询到用户角色，请授权后访问!");
            }
        }catch (Exception e){
            e.printStackTrace();
        }

        String localUsername = "portal_" + portalUserId;
        sysUser.setUserName(localUsername);
        sysUser.setNickName(portalUser.getName());
        sysUser.setIdCard(portalUser.getIdcardno());
        sysUser.setUserId(portalUserId);
        sysUserService.updateUser(sysUser);
    }

    /**
     * 记录登录信息（复用若依原生逻辑，直接复制过来）
     */
    private void recordLoginInfo(Long userId) {
        SysUser sysUser = new SysUser();
        sysUser.setUserId(userId);
        sysUser.setLoginIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
        sysUser.setLoginDate(new Date());
        sysUserService.updateUserProfile(sysUser);
    }

    /**
     * Long 转 int
     */
    private int safeLongToInt(Long value) {
        if (value == null) {
            throw new ServiceException("门户 Token 有效期不能为空");
        }
        // 校验是否超过 int 最大值（实际场景几乎不会触发）
        if (value > Integer.MAX_VALUE) {
            return Integer.MAX_VALUE;
        }
        // 校验是否为负数（无效有效期）
        if (value < 0) {
            throw new ServiceException("门户 Token 有效期不能为负数");
        }
        return value.intValue();
    }
}