package shop.alien.gateway.service;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import shop.alien.entity.result.R;
import shop.alien.entity.store.LifeUser;
import shop.alien.entity.store.LifeUserThirdBind;
import shop.alien.entity.store.vo.LifeUserThirdBindInfoVo;
import shop.alien.gateway.config.LifeUserThirdBindProperties;
import shop.alien.gateway.mapper.LifeUserGatewayMapper;
import shop.alien.gateway.thirdparty.WeChatMobileOAuthClient;
import shop.alien.mapper.LifeUserThirdBindMapper;

import java.util.Date;

@Service
@RequiredArgsConstructor
public class LifeUserThirdBindService {

    private final LifeUserGatewayMapper lifeUserMapper;
    private final LifeUserThirdBindMapper thirdBindMapper;
    private final LifeUserThirdBindProperties thirdBindProperties;
    private final WeChatMobileOAuthClient weChatMobileOAuthClient;

    public Integer resolveLifeUserId(JSONObject sub) {
        if (sub == null) {
            return null;
        }
        String userType = sub.getString("userType");
        if (!"user".equals(userType) && !"miniprogram_user".equals(userType)) {
            return null;
        }
        String userIdStr = sub.getString("userId");
        if (StringUtils.isNotBlank(userIdStr)) {
            try {
                return Integer.parseInt(userIdStr);
            } catch (NumberFormatException ignored) {
                // fall through
            }
        }
        String phone = sub.getString("phone");
        if (StringUtils.isNotBlank(phone)) {
            LifeUser u = lifeUserMapper.selectOne(
                    new LambdaQueryWrapper<LifeUser>().eq(LifeUser::getUserPhone, phone).last("LIMIT 1"));
            return u != null ? u.getId() : null;
        }
        return null;
    }

    public LifeUserThirdBindInfoVo getThirdBindInfo(Integer lifeUserId) {
        LifeUserThirdBindInfoVo vo = new LifeUserThirdBindInfoVo();
        vo.setLifeUserId(lifeUserId);
        LifeUser user = lifeUserMapper.selectById(lifeUserId);
        if (user != null) {
            vo.setLifeUserPhone(user.getUserPhone());
        }
        LifeUserThirdBind wechat = latestRow(lifeUserId, LifeUserThirdBind.PLATFORM_WECHAT);
        LifeUserThirdBind alipay = latestRow(lifeUserId, LifeUserThirdBind.PLATFORM_ALIPAY);
        vo.setWechat(toWechat(wechat));
        vo.setAlipay(toAlipay(alipay));
        return vo;
    }

    private LifeUserThirdBind latestRow(Integer lifeUserId, String platform) {
        return thirdBindMapper.selectOne(
                new LambdaQueryWrapper<LifeUserThirdBind>()
                        .eq(LifeUserThirdBind::getLifeUserId, lifeUserId)
                        .eq(LifeUserThirdBind::getPlatform, platform)
                        .orderByDesc(LifeUserThirdBind::getUpdatedTime)
                        .last("LIMIT 1"));
    }

    @Transactional(rollbackFor = Exception.class)
    public R<LifeUserThirdBindInfoVo> bindWechat(Integer lifeUserId, String code,
                                                 String nickName, String bindPhone) {
        if (!thirdBindProperties.isEnabled()) {
            return R.fail("第三方绑定功能已关闭");
        }
        String guard = assertUserBindableOrMsg(lifeUserId);
        if (guard != null) {
            return R.fail(guard);
        }
        WeChatMobileOAuthClient.OAuthExchangeResult session = weChatMobileOAuthClient.exchangeCode(code);
        if (!session.isSuccess()) {
            String msg = session.getErrmsg();
            if (session.getErrcode() != null) {
                msg = StringUtils.defaultString(msg) + "（errcode=" + session.getErrcode() + "）";
            }
            return R.fail(StringUtils.defaultIfBlank(msg, "微信授权失败"));
        }
        String openid = session.getOpenid();
        if (isWechatOpenidBoundToOtherUser(openid, lifeUserId)) {
            return R.fail("该微信已绑定其他账号");
        }
        String label = StringUtils.trimToNull(nickName);
        String phone = StringUtils.trimToNull(bindPhone);
        upsertWechatRow(lifeUserId, openid, session.getUnionid(), label, phone);
        return R.data(getThirdBindInfo(lifeUserId));
    }

    @Transactional(rollbackFor = Exception.class)
    public R<LifeUserThirdBindInfoVo> unbindWechat(Integer lifeUserId) {
        if (!thirdBindProperties.isEnabled()) {
            return R.fail("第三方绑定功能已关闭");
        }
        String guard = assertUserBindableOrMsg(lifeUserId);
        if (guard != null) {
            return R.fail(guard);
        }
        LifeUserThirdBind row = latestRow(lifeUserId, LifeUserThirdBind.PLATFORM_WECHAT);
        if (row == null) {
            return R.fail("当前未绑定微信");
        }
        thirdBindMapper.deleteById(row.getId());
        return R.data(getThirdBindInfo(lifeUserId));
    }

    private String assertUserBindableOrMsg(Integer lifeUserId) {
        LifeUser user = lifeUserMapper.selectById(lifeUserId);
        if (user == null) {
            return "用户不存在";
        }
        if (user.getLogoutFlag() != null && user.getLogoutFlag() == 1) {
            return "账号已注销，无法操作绑定";
        }
        if (user.getIsBanned() != null && user.getIsBanned() == 1) {
            return "账号已被封禁，无法操作绑定";
        }
        return null;
    }

    private boolean isWechatOpenidBoundToOtherUser(String openid, Integer lifeUserId) {
        if (StringUtils.isBlank(openid)) {
            return false;
        }
        long n = thirdBindMapper.selectCount(
                new LambdaQueryWrapper<LifeUserThirdBind>()
                        .eq(LifeUserThirdBind::getPlatform, LifeUserThirdBind.PLATFORM_WECHAT)
                        .eq(LifeUserThirdBind::getOpenid, openid)
                        .ne(LifeUserThirdBind::getLifeUserId, lifeUserId));
        return n > 0;
    }

    private void upsertWechatRow(Integer lifeUserId, String openid, String unionid, String accountLabel, String bindPhone) {
        Date now = new Date();
        LifeUserThirdBind existing = latestRow(lifeUserId, LifeUserThirdBind.PLATFORM_WECHAT);
        if (existing != null) {
            existing.setOpenid(openid);
            existing.setUnionid(unionid);
            if (accountLabel != null) {
                existing.setAccountLabel(accountLabel);
            }
            if (bindPhone != null) {
                existing.setBindPhone(bindPhone);
            }
            existing.setUpdatedTime(now);
            thirdBindMapper.updateById(existing);
        } else {
            LifeUserThirdBind row = new LifeUserThirdBind();
            row.setLifeUserId(lifeUserId);
            row.setPlatform(LifeUserThirdBind.PLATFORM_WECHAT);
            row.setOpenid(openid);
            row.setUnionid(unionid);
            row.setAccountLabel(accountLabel);
            row.setBindPhone(bindPhone);
            row.setDeleteFlag(0);
            row.setCreatedTime(now);
            row.setUpdatedTime(now);
            thirdBindMapper.insert(row);
        }
    }

    private static LifeUserThirdBindInfoVo.WechatBindItem toWechat(LifeUserThirdBind row) {
        LifeUserThirdBindInfoVo.WechatBindItem it = new LifeUserThirdBindInfoVo.WechatBindItem();
        if (row == null) {
            it.setBound(false);
            return it;
        }
        it.setBound(true);
        String label = row.getAccountLabel();
        it.setNickName(label);
        it.setAccountLabel(label);
        it.setOpenid(row.getOpenid());
        it.setUnionid(row.getUnionid());
        it.setBindPhone(row.getBindPhone());
        return it;
    }

    private static LifeUserThirdBindInfoVo.AlipayBindItem toAlipay(LifeUserThirdBind row) {
        LifeUserThirdBindInfoVo.AlipayBindItem it = new LifeUserThirdBindInfoVo.AlipayBindItem();
        if (row == null) {
            it.setBound(false);
            return it;
        }
        it.setBound(true);
        String label = row.getAccountLabel();
        it.setNickName(label);
        it.setAccountLabel(label);
        it.setAlipayUserId(row.getAlipayUserId());
        it.setBindPhone(row.getBindPhone());
        return it;
    }
}
