package com.huimv.employment.service.auth;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.huimv.employment.common.exception.BizException;
import com.huimv.employment.common.exception.ErrorCode;
import com.huimv.employment.common.util.AesUtils;
import com.huimv.employment.common.util.MobileUtils;
import com.huimv.employment.dao.entity.FeUser;
import com.huimv.employment.dao.mapper.FeUserMapper;
import com.huimv.employment.service.auth.dto.LoginResponse;
import com.huimv.employment.service.auth.dto.SmsLoginRequest;
import com.huimv.employment.service.auth.dto.SmsSendRequest;
import com.huimv.employment.service.auth.dto.SmsSendResponse;
import com.huimv.employment.service.auth.dto.WechatLoginRequest;
import com.huimv.employment.service.config.SecurityProperties;
import com.huimv.employment.service.sms.SmsCodeService;
import com.huimv.employment.service.wechat.WeChatMiniAppService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;

/**
 * 认证业务：短信登录、微信登录（账密见 {@link PasswordAuthService}）。
 */
@Service
public class AuthService {

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

    private static final String STATUS_ACTIVE = "active";
    private static final String STATUS_DISABLED = "disabled";

    private final FeUserMapper feUserMapper;
    private final SmsCodeService smsCodeService;
    private final JwtTokenService jwtTokenService;
    private final SecurityProperties securityProperties;
    private final WeChatMiniAppService weChatMiniAppService;

    public AuthService(FeUserMapper feUserMapper,
                       SmsCodeService smsCodeService,
                       JwtTokenService jwtTokenService,
                       SecurityProperties securityProperties,
                       WeChatMiniAppService weChatMiniAppService) {
        this.feUserMapper = feUserMapper;
        this.smsCodeService = smsCodeService;
        this.jwtTokenService = jwtTokenService;
        this.securityProperties = securityProperties;
        this.weChatMiniAppService = weChatMiniAppService;
    }

    public SmsSendResponse sendLoginCode(SmsSendRequest request) {
        SmsCodeService.SmsSendResult result = smsCodeService.sendLoginCode(request.getMobile());
        return new SmsSendResponse(result.isMockEnabled(), result.getExpireSeconds());
    }

    @Transactional(rollbackFor = Exception.class)
    public LoginResponse loginBySms(SmsLoginRequest request) {
        smsCodeService.verifyLoginCode(request.getMobile(), request.getCode());

        String mobileEnc = AesUtils.encrypt(request.getMobile(), securityProperties.getMobileAesKey());
        String mobileMask = MobileUtils.mask(request.getMobile());

        FeUser user = findOrCreateByMobile(mobileEnc, mobileMask, request.getUserType());
        ensureUserActive(user);
        ensureUserTypeMatch(user, request.getUserType());
        user.setMobileMask(mobileMask);
        touchLogin(user);
        feUserMapper.updateById(user);

        return buildLoginResponse(user);
    }

    @Transactional(rollbackFor = Exception.class)
    public LoginResponse loginByWechat(WechatLoginRequest request) {
        WeChatMiniAppService.WxSession session = weChatMiniAppService.code2Session(request.getCode());

        FeUser user = findOrCreateByOpenid(session, request.getUserType());
        ensureUserActive(user);
        ensureUserTypeMatch(user, request.getUserType());
        user.setUnionid(session.getUnionid());
        user.setSessionKey(session.getSessionKey());
        touchLogin(user);
        feUserMapper.updateById(user);

        return buildLoginResponse(user);
    }

    /**
     * 按加密手机号查找或创建用户；并发 insert 冲突时重查已存在记录。
     */
    private FeUser findOrCreateByMobile(String mobileEnc, String mobileMask, String userType) {
        FeUser user = findByMobileEnc(mobileEnc);
        if (user != null) {
            return user;
        }
        try {
            return createUserBySms(mobileEnc, mobileMask, userType);
        } catch (DuplicateKeyException ex) {
            log.debug("Concurrent sms login insert for mobile_enc, re-select", ex);
            user = findByMobileEnc(mobileEnc);
            if (user == null) {
                log.error("DuplicateKey on mobile_enc but re-select returned null", ex);
                throw new BizException(ErrorCode.INTERNAL_ERROR);
            }
            return user;
        }
    }

    /**
     * 按 openid 查找或创建用户；并发 insert 冲突时重查已存在记录。
     */
    private FeUser findOrCreateByOpenid(WeChatMiniAppService.WxSession session, String userType) {
        FeUser user = findByOpenid(session.getOpenid());
        if (user != null) {
            return user;
        }
        try {
            return createUserByWechat(session, userType);
        } catch (DuplicateKeyException ex) {
            log.debug("Concurrent wechat login insert for openid, re-select", ex);
            user = findByOpenid(session.getOpenid());
            if (user == null) {
                log.error("DuplicateKey on openid but re-select returned null", ex);
                throw new BizException(ErrorCode.INTERNAL_ERROR);
            }
            return user;
        }
    }

    private FeUser findByMobileEnc(String mobileEnc) {
        return feUserMapper.selectOne(new LambdaQueryWrapper<FeUser>()
                .eq(FeUser::getMobileEnc, mobileEnc)
                .last("LIMIT 1"));
    }

    private FeUser findByOpenid(String openid) {
        return feUserMapper.selectOne(new LambdaQueryWrapper<FeUser>()
                .eq(FeUser::getOpenid, openid)
                .last("LIMIT 1"));
    }

    private FeUser createUserBySms(String mobileEnc, String mobileMask, String userType) {
        LocalDateTime now = LocalDateTime.now();
        FeUser user = new FeUser();
        user.setUserType(userType);
        user.setMobileEnc(mobileEnc);
        user.setMobileMask(mobileMask);
        user.setStatus(STATUS_ACTIVE);
        user.setPwdFailedCount(0);
        user.setLastLoginAt(now);
        user.setCreateTime(now);
        user.setUpdateTime(now);
        user.setDelFlag(0);
        feUserMapper.insert(user);
        return user;
    }

    private FeUser createUserByWechat(WeChatMiniAppService.WxSession session, String userType) {
        LocalDateTime now = LocalDateTime.now();
        FeUser user = new FeUser();
        user.setOpenid(session.getOpenid());
        user.setUnionid(session.getUnionid());
        user.setSessionKey(session.getSessionKey());
        user.setUserType(userType);
        user.setStatus(STATUS_ACTIVE);
        user.setPwdFailedCount(0);
        user.setLastLoginAt(now);
        user.setCreateTime(now);
        user.setUpdateTime(now);
        user.setDelFlag(0);
        feUserMapper.insert(user);
        return user;
    }

    private void touchLogin(FeUser user) {
        user.setLastLoginAt(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
    }

    private void ensureUserActive(FeUser user) {
        if (STATUS_DISABLED.equals(user.getStatus())) {
            throw new BizException(ErrorCode.USER_DISABLED);
        }
    }

    /**
     * 用户身份类型仅在首次创建时确定，后续登录不可切换企业主/临时工。
     */
    private void ensureUserTypeMatch(FeUser user, String requestedUserType) {
        if (!requestedUserType.equals(user.getUserType())) {
            throw new BizException(ErrorCode.USER_TYPE_MISMATCH);
        }
    }

    private LoginResponse buildLoginResponse(FeUser user) {
        JwtTokenService.TokenIssueResult token = jwtTokenService.issueToken(user.getId(), user.getUserType());
        LoginResponse response = new LoginResponse();
        response.setToken(token.getToken());
        response.setExpiresIn(token.getExpiresIn());
        response.setUserId(user.getId());
        response.setUserType(user.getUserType());
        response.setMobileMask(user.getMobileMask());
        response.setNickname(user.getNickname());
        response.setPasswordSet(StringUtils.hasText(user.getPasswordHash()));
        return response;
    }
}
