package com.ruoyi.framework.web.service;

import com.ruoyi.cms.service.CompanyContactService;
import com.ruoyi.cms.service.IAppUserService;
import com.ruoyi.cms.service.ICompanyService;
import com.ruoyi.cms.util.StringUtil;
import com.ruoyi.cms.util.oauth.OauthClient;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.entity.AppUser;
import com.ruoyi.common.core.domain.entity.Company;
import com.ruoyi.common.core.domain.entity.CompanyContact;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.domain.entity.tymh.nwToken.PortalTokenCacheDTO;
import com.ruoyi.common.core.domain.entity.tymh.wwToken.WwTokenResult;
import com.ruoyi.common.core.domain.entity.tymh.wwToken.WwTyInfo;
import com.ruoyi.common.core.domain.entity.tymh.wwToken.WwUserLogin;
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.util.CollectionUtils;
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.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class OauthLoginHlwService {

    @Autowired
    private OauthClient oauthClient;

    @Autowired
    private TokenService tokenService;
    @Autowired
    private RedisCache redisCache;
    @Autowired
    private ISysUserService sysUserService;
    @Autowired
    private UserDetailsService userDetailsService;
    @Autowired
    private ICompanyService companyService;
    @Autowired
    private CompanyContactService companyContactService;
    @Autowired
    private IAppUserService appUserService;

    // Redis缓存：门户UserID → 若依本地用户名（避免重复匹配数据库）
    private static final String REDIS_KEY_PORTAL_USER_MAPPING = "hlw:user:mapping:";
    // 门户 Token 存储前缀（Redis 键：门户 userId → 门户 Token 信息）
    private static final String REDIS_KEY_PORTAL_TOKEN = "hlw:token:";

    final private int expireTime=30;
    protected static final long MILLIS_SECOND = 1000;
    protected static final long MILLIS_MINUTE = 60 * MILLIS_SECOND;

    /**
     * OAuth 登录流程：通过授权码获取系统令牌
     * @return 系统内部令牌（供前端后续使用）
     */
    public String getWwTjmhToken(WwUserLogin wwUserLogin){
        try {
            //获取门户token
            WwTokenResult wwTokenResult = oauthClient.wwGetToken(wwUserLogin);
            String wwToken=wwTokenResult.getAccessToken();
            System.out.println("wwToken======================="+wwToken);
            if (StringUtils.isBlank(wwToken)) {
                throw new ServiceException("获取门户 Token 失败：" + wwToken);
            }
            //获取门户userInfo
            WwTyInfo portalUser = oauthClient.wwGetUserInfo(wwTokenResult);
            //匹配/创建本地用户
            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(), wwToken);
            recordLoginInfo(loginUser.getUserId());
            return tokenService.createToken(loginUser);
        } catch (Exception e) {
            throw new ServiceException("OAuth 登录失败：" + e.getMessage());
        }
    }

    /**
     * 匹配/创建本地用户，返回若依本地用户名
     */
    private String getOrCreateLocalUser(WwTyInfo wwTyInfo) {
        String idCard="";
        switch (wwTyInfo.getUsertype()){
            case "1"://个人
                idCard=wwTyInfo.getIdno();
                break;
            default:
                idCard=wwTyInfo.getEnterprisecode();
        }
        // 先从Redis查询缓存的本地用户名
        String cacheKey = REDIS_KEY_PORTAL_USER_MAPPING + idCard;
        String localUsername = redisCache.getCacheObject(cacheKey);
        if (StringUtils.isNotBlank(localUsername)) {
            if ("hlw_".equals(localUsername)) {
                localUsername = String.format("%s%s", localUsername, idCard);
            }
            updateUserInfo(wwTyInfo);

            return localUsername;
        }

        SysUser localUser=sysUserService.selectUserByIdCard(wwTyInfo.getIdno());
        if (localUser == null) {
            // 本地无用户，自动创建
            localUser = createLocalUser(wwTyInfo);
            // 缓存门户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 parseStringToLoing(String portalUserIdStr) {
        try {
            return Long.parseLong(portalUserIdStr);
        } catch (NumberFormatException e) {
            throw new ServiceException("门户用户ID格式错误：" + portalUserIdStr);
        }
    }

    /**
     * 自动创建本地用户
     */
    private SysUser createLocalUser(WwTyInfo wwTyInfo) {
        //移动端用户
        AppUser appUserParm=new AppUser();
        appUserParm.setIsRecommend(1);
        //pc端
        SysUser newUser = new SysUser();
        String localUsername=StringUtil.USER_KEY+wwTyInfo.getIdno();
        switch (wwTyInfo.getUsertype()) {
            case "1"://个人
                newUser.setNickName(wwTyInfo.getName());
                newUser.setIdCard(wwTyInfo.getIdno());
                newUser.setRoleIds(new Long[]{parseStringToLoing(StringUtil.SYS_QZZ)});
                //移动端
                appUserParm.setPhone(wwTyInfo.getPhone());
                appUserParm.setIdCard(wwTyInfo.getIdno());
                appUserParm.setName(wwTyInfo.getName());
                break;
            default://单位
                newUser.setNickName(wwTyInfo.getEnterprisename());
                newUser.setIdCard(wwTyInfo.getEnterprisecode());
                newUser.setRoleIds(new Long[]{parseStringToLoing(StringUtil.SYS_QY)});
                //修改企业和企业联系人
                updateCompanyContact(wwTyInfo);

                //移动端
                appUserParm.setPhone(wwTyInfo.getPhone());
                appUserParm.setIdCard(wwTyInfo.getEnterprisecode());
                appUserParm.setName(wwTyInfo.getEnterprisename());
        }
        newUser.setPassword(SecurityUtils.encryptPassword("123456"));
        newUser.setDelFlag("0");
        newUser.setUserName(localUsername);
        sysUserService.insertUser(newUser);
        //插入app_user
        AppUser appUser=appUserService.selectAppuserByIdcard(wwTyInfo.getIdno());
        if(appUser!=null){
            appUserParm.setUserId(appUser.getUserId());
            appUserService.updateAppUser(appUserParm);
        }else{
            appUserService.insertAppUser(appUserParm);
        }
        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, String accessToken) {
        String redisKey = REDIS_KEY_PORTAL_TOKEN + portalUserId;

        PortalTokenCacheDTO tokenCache = new PortalTokenCacheDTO();
        tokenCache.setAccessToken(accessToken);
        tokenCache.setExpireTimestamp(System.currentTimeMillis() + expireTime * MILLIS_MINUTE);

        redisCache.setCacheObject(redisKey, tokenCache, expireTime, TimeUnit.SECONDS);
    }

    /**
     * 记录登录信息（复用若依原生逻辑，直接复制过来）
     */
    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);
    }

    /**
     * 获取互联网token
     * @param wwTokenResult
     * @return
     */
    public String getWwTjmHlwToken(WwTokenResult wwTokenResult){
        try {
            //获取门户userInfo
            WwTyInfo portalUser = oauthClient.wwGetUserInfo(wwTokenResult);
            //匹配/创建本地用户
            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(), wwTokenResult.getAccessToken());
            recordLoginInfo(loginUser.getUserId());
            return tokenService.createToken(loginUser);
        } catch (Exception e) {
            throw new ServiceException("OAuth 登录失败：" + e.getMessage());
        }
    }

    /**
     * 修改个人信息
     * @param wwTyInfo
     */
    private void updateUserInfo(WwTyInfo wwTyInfo){
        //移动端用户
        AppUser appUserParm=new AppUser();
        appUserParm.setIsRecommend(1);
        //pc端
        String code="";
        SysUser sysUser=new SysUser();
        switch (wwTyInfo.getUsertype()){
            case "1":
                sysUser.setNickName(wwTyInfo.getName());
                sysUser.setIdCard(wwTyInfo.getIdno());
                sysUser.setRoleIds(new Long[]{parseStringToLoing(StringUtil.SYS_QZZ)});
                //移动端
                appUserParm.setPhone(wwTyInfo.getPhone());
                appUserParm.setIdCard(wwTyInfo.getIdno());
                appUserParm.setName(wwTyInfo.getName());
                appUserParm.setIsCompanyUser(StringUtil.IS_JOB_REQUEST_USER);
                code=wwTyInfo.getIdno();
                break;
            default:
                sysUser.setNickName(wwTyInfo.getEnterprisename());
                sysUser.setIdCard(wwTyInfo.getEnterprisecode());
                sysUser.setRoleIds(new Long[]{parseStringToLoing(StringUtil.SYS_QY)});
                //企业联系人->现根据社会信用代码查询企业信息
                updateCompanyContact(wwTyInfo);
                //移动端
                String phone = StringUtils.isNotBlank(wwTyInfo.getPhone()) ? wwTyInfo.getPhone() : wwTyInfo.getContactphone();
                appUserParm.setPhone(phone);
                appUserParm.setIdCard(wwTyInfo.getEnterprisecode());
                appUserParm.setName(wwTyInfo.getEnterprisename());
                appUserParm.setIsCompanyUser(StringUtil.IS_COMPANY_USER);
                code=wwTyInfo.getEnterprisecode();
        }
        String localUsername=StringUtil.USER_KEY+code;
        //查询用户角色
        sysUser.setUserName(localUsername);
        //查询用户id
        SysUser parmUser=sysUserService.selectUserByIdCard(code);
        sysUser.setUserId(parmUser.getUserId());
        sysUserService.updateUser(sysUser);
        //插入app_user
        AppUser appUser=appUserService.selectAppuserByIdcard(code);
        if(appUser!=null){
            appUserParm.setUserId(appUser.getUserId());
            appUserService.updateAppUser(appUserParm);
        }else{
            appUserService.insertAppUser(appUserParm);
        }
    }

    /**
     * 修改联系人信息
     * @param wwTyInfo
     */
    public void updateCompanyContact(WwTyInfo wwTyInfo) {
        if (wwTyInfo == null) {
            throw new IllegalArgumentException("参数WwTyInfo不能为空");
        }
        String enterpriseCode = wwTyInfo.getEnterprisecode();
        String enterpriseName = wwTyInfo.getEnterprisename();

        String contactPerson = wwTyInfo.getContactperson();
        String contactPhone = wwTyInfo.getContactphone();

        Company company = companyService.queryCodeCompany(enterpriseCode);

        CompanyContact contact = new CompanyContact();
        contact.setContactPerson(contactPerson);
        contact.setContactPersonPhone(contactPhone);

        if (Objects.nonNull(company)) {
            Company updateCompany = new Company();
            updateCompany.setCompanyId(company.getCompanyId());
            updateCompany.setCode(enterpriseCode);
            updateCompany.setName(enterpriseName);
            companyService.updateCompany(updateCompany);

            contact.setCompanyId(company.getCompanyId());
            List<CompanyContact> existingContacts = companyContactService.getSelectList(contact);

            List<CompanyContact> contactList = new ArrayList<>();
            if (!CollectionUtils.isEmpty(existingContacts)) {
                contact.setId(existingContacts.get(0).getId());
                contactList.add(contact);
                companyContactService.insertUpadteCompanyContact(contactList);
            } else {
                companyContactService.insertContact(contact);
            }
        } else {
            Company newCompany = new Company();
            newCompany.setCode(enterpriseCode);
            newCompany.setName(enterpriseName);
            companyService.insertCompany(newCompany);

            if (Objects.nonNull(newCompany.getCompanyId())) {
                contact.setCompanyId(newCompany.getCompanyId());
                companyContactService.insertContact(contact);
            } else {
                throw new RuntimeException("新增企业失败：社会信用代码=" + enterpriseCode + "，未返回有效企业ID");
            }
        }
    }
}