package com.kym.service.miniapp.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.github.pagehelper.PageHelper;
import com.github.yulichang.base.MPJBaseServiceImpl;
import com.kym.common.R;
import com.kym.common.config.WxConfig;
import com.kym.common.constant.ResponseEnum;
import com.kym.common.enums.WxApi;
import com.kym.common.exception.BusinessException;
import com.kym.common.utils.CommUtil;
import com.kym.common.utils.HttpUtil;
import com.kym.entity.admin.Activity;
import com.kym.entity.admin.RechargeRights;
import com.kym.entity.admin.queryParams.CommonQueryParam;
import com.kym.entity.admin.vo.ActivityVo;
import com.kym.entity.admin.vo.CustomUserVo;
import com.kym.entity.common.PageBean;
import com.kym.entity.miniapp.*;
import com.kym.entity.miniapp.queryParams.WxLoginParams;
import com.kym.entity.miniapp.vo.UserVo;
import com.kym.entity.wechat.WxPhoneNum;
import com.kym.mapper.miniapp.MpRelationMapper;
import com.kym.mapper.miniapp.UserMapper;
import com.kym.service.admin.ActivityService;
import com.kym.service.admin.ActivityStationService;
import com.kym.service.admin.BannerService;
import com.kym.service.admin.RechargeRightsService;
import com.kym.service.miniapp.AccountService;
import com.kym.service.miniapp.CarsService;
import com.kym.service.miniapp.RefundLogService;
import com.kym.service.miniapp.UserService;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static java.util.Map.of;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author skyline
 * @since 2023-06-27
 */
@Service
@DS("db-miniapp")
public class UserServiceImpl extends MPJBaseServiceImpl<UserMapper, User> implements UserService {
    private static final Logger LOGGER = LoggerFactory.getLogger(UserServiceImpl.class);
    private final WxConfig wxConfig;
    private final AccountService accountService;
    private final RefundLogService refundLogService;
    private final CarsService carsService;
    private final RechargeRightsService rechargeRightsService;
    private final ActivityService activityService;

    private final ActivityStationService activityStationService;
    private final BannerService bannerService;

    private final MpRelationMapper mpRelationMapper;


    public UserServiceImpl(WxConfig wxConfig, @Lazy AccountService accountService, RefundLogService refundLogService,
                           CarsService carsService, RechargeRightsService rechargeRightsService, @Lazy ActivityService activityService, ActivityStationService activityStationService,
                           BannerService bannerService, MpRelationMapper mpRelationMapper) {
        this.wxConfig = wxConfig;
        this.accountService = accountService;
        this.refundLogService = refundLogService;
        this.carsService = carsService;
        this.rechargeRightsService = rechargeRightsService;
        this.activityService = activityService;
        this.activityStationService = activityStationService;
        this.bannerService = bannerService;
        this.mpRelationMapper = mpRelationMapper;
    }


    @Transactional(rollbackFor = Exception.class)
    @SneakyThrows
    @Override
    public R<?> wxLogin(WxLoginParams params) {
        // 微信登录
        var json = HttpUtil.getJson(WxApi.WX_MP_LOGIN.getApi(), Map.of(
                "appid", wxConfig.getAppid(),
                "secret", wxConfig.getSecret(),
                "js_code", params.getCode()
        ));

        Integer errorCode = json.getInteger("errcode");
        if (errorCode == null) {
            // 业务逻辑处理
            var openid = json.getString("openid");
            var unionid = json.getString("unionid") == null ? "" : json.getString("unionid");
            // 判断有没有，没有就新增
            var user = baseMapper.selectOne(new QueryWrapper<User>().eq("openid", openid));
            if (user != null) {
                // 登录逻辑
                if (CommUtil.isNotEmptyAndNull(unionid) && CommUtil.isEmptyOrNull(user.getUnionid())) {
                    user.setUnionid(unionid);
                }
                return handleLogin(user);
            } else {
                // 注册逻辑
                // 新增用户
                var newUser = new User();
                newUser.setOpenid(openid);
                newUser.setUnionid(unionid);
                // 手机号解密:先获取access_token,再请求手机号信息
                // access_token获取
                var accessTokenJson = HttpUtil.getJson(WxApi.WX_GET_ACCESS_TOKEN.getApi(), Map.of("appid", wxConfig.getAppid(), "secret", wxConfig.getSecret()));
                var accessToken = accessTokenJson.getString("access_token");
                var wxPhoneNum = HttpUtil.post(WxApi.WX_MP_GET_PHONE.getApi().replace("ACCESS_TOKEN", accessToken), Map.of("code", params.getPhoneCode()), WxPhoneNum.class);
                var mobilePhone = wxPhoneNum.getPhone_info().getPurePhoneNumber();
                newUser.setMobilePhone(mobilePhone);
                newUser.setUsername(mobilePhone);
                newUser.setAvatar(params.getAvatar());
                newUser.setNickname(params.getNickname());
                baseMapper.insert(newUser);
                // 创建用户账户
                var account = new Account();
                account.setUserId(newUser.getId());
                accountService.save(account);
                // 登录逻辑
                return handleLogin(newUser);
            }

        } else {
            // 抛出异常
            LOGGER.error("微信登录异常,错误码{}，异常信息{}", errorCode, json.getString("errmsg"));
            throw new BusinessException(ResponseEnum.WX_MP_LOGIN_ERROR);
        }
    }

    /**
     * 获取当前登录用户信息
     *
     * @return
     */
    @Override
    public UserVo getMe() {
        var userId = StpUtil.getLoginIdAsLong();
        var userVo = baseMapper.getMe(userId);
        var car = carsService.lambdaQuery().eq(Cars::getUserId, userId).eq(Cars::getIsDefault, 1).one();
        if (car != null) {
            userVo.setDefaultPlateNo(car.getPlateNo());
            userVo.setVin(car.getVin());
        }

        // 可以参加的活动-充值权益
        DynamicDataSourceContextHolder.push("db-admin");
        var activityList = activityService.lambdaQuery().eq(Activity::getStatus, Activity.STATUS_进行中).list();
        if (CommUtil.isNotEmptyAndNull(activityList)) {
            var rechargeRight = rechargeRightsService.lambdaQuery().eq(RechargeRights::getStatus, Activity.STATUS_进行中).list();
            var bannerList = bannerService.list();
            var activity2Station = activityStationService.getActivityVoList(activityList.stream().map(Activity::getId).toList()).stream().collect(Collectors.toMap(ActivityVo::getActivityId, ActivityVo::getStationList));
            var voList = activityList.stream().map(activity -> {
                var vo = new ActivityVo();
                BeanUtils.copyProperties(activity, vo);
                vo.setRechargeRightsList(rechargeRight.stream().filter(rights -> rights.getActivityId().equals(activity.getId())).toList());
                vo.setBannerList(bannerList.stream().filter(banner -> banner.getActivityId().equals(activity.getId())).toList());
                vo.setStationList(activity2Station.get(activity.getId()));
                return vo;
            }).toList();
            userVo.setActivityList(voList);
        }
        DynamicDataSourceContextHolder.poll();
        return userVo;
    }


    /**
     * 登录逻辑处理
     *
     * @param user
     * @return
     */
    private R<?> handleLogin(User user) {
        StpUtil.login(user.getId());
        // 用户名存入session，统一日志读取使用
        StpUtil.getSession().set("openid", user.getOpenid());
        StpUtil.getSession().set("unionid", user.getUnionid());
        StpUtil.getSession().set("userId", user.getId());
        StpUtil.getSession().set("username", user.getUsername());
        StpUtil.getSession().set("mobilePhone", user.getMobilePhone());
        user.setLastLoginTime(LocalDateTime.now());
        updateById(user);
        LOGGER.info("用户：{}/{}登录成功，tokenName:{},tokenValue:{}", user.getMobilePhone(), user.getId(), StpUtil.getTokenName(), StpUtil.getTokenValue());
        updateUnionid();
        return R.success(of("userId", user.getId(), "satoken", StpUtil.getTokenValue()));
    }

    @Async
    protected void updateUnionid() {
        var unionid = StpUtil.getSession().getString("unionid");
        if (CommUtil.isNotEmptyAndNull(unionid)) {
            // 匹配公众号用户
            var wrapper = new LambdaUpdateWrapper<MpRelation>().eq(MpRelation::getUnionid, unionid).set(MpRelation::getUserId, StpUtil.getLoginIdAsLong()).set(MpRelation::getOpenid, StpUtil.getSession().getString("openid"));
            mpRelationMapper.update(null, wrapper);
        }
    }


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateUser(UserVo userVo) {
        userVo.setId(StpUtil.getSession().getLong("userId"));
        var user = new User();
        BeanUtils.copyProperties(userVo, user);
        if (!CommUtil.isEmptyOrNull(user.getAvatar()) || !CommUtil.isEmptyOrNull(user.getMobilePhone()) || !CommUtil.isEmptyOrNull(user.getNickname())) {
            baseMapper.updateById(user);
        }
        // 更新车牌信息
        if (userVo.getDefaultPlateNo() != null) {
            var car = new Cars();
            car.setUserId(userVo.getId());
            car.setPlateNo(userVo.defaultPlateNo);
            // 设置为默认
            car.setIsDefault(true);
            if (userVo.getVin() != null) {
                car.setVin(userVo.getVin());
            }
            // 将用户名下其他车辆设为非默认
            var cars = carsService.listByMap(Map.of("user_id", userVo.getId()));
            cars.stream().filter(c -> !userVo.getDefaultPlateNo().equals(c.getEngineNo())).peek(s -> s.setIsDefault(false)).collect(Collectors.toList());
            carsService.updateBatchById(cars);
            var wrapper = new QueryWrapper<Cars>();
            wrapper.eq("plate_no", userVo.getDefaultPlateNo());
            // MyBatis-Plus 3.5.9+ saveOrUpdate(T, Wrapper) 方法已移除，改为先查询再保存或更新
            var existCar = carsService.getOne(wrapper);
            if (existCar != null) {
                car.setId(existCar.getId());
                carsService.updateById(car);
            } else {
                carsService.save(car);
            }
        }
    }

    @Override
    public PageBean<UserVo> listUserVo(Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        var userVoList = list().stream().map(user -> {
            var userVo = new UserVo();
            BeanUtils.copyProperties(user, userVo);
            return userVo;
        }).collect(Collectors.toList());
        return new PageBean<>(userVoList);
    }


    /**
     * 分页查询用户列表
     *
     * @param params
     * @return
     */
    @Override
    public PageBean<CustomUserVo> listCustomUser(CommonQueryParam params) {
        List<Long> userIds = null;
        if (!CommUtil.isEmptyOrNull(params.getMobilePhone())) {
            userIds = lambdaQuery().
                    eq(User::getMobilePhone, params.getMobilePhone())
                    .eq(!CommUtil.isEmptyOrNull(params.getStatus()), User::getStatus, params.getStatus()).list().stream().map(User::getId).toList();
            if (CommUtil.isEmptyOrNull(userIds)) {
                return new PageBean<>();
            }
        }
        PageHelper.startPage(params.getPageNum(), params.getPageSize());
        var result = baseMapper.listUser(userIds);
        var page = new PageBean<>(result);
        // 用户余额，退款次数，退款金额
        var account = accountService.lambdaQuery().in(Account::getUserId, result.stream().map(CustomUserVo::getUserId).toList()).list();
        var user2Balance = account.stream().collect(Collectors.groupingBy(Account::getUserId, Collectors.summingInt(Account::getBalance)));
        var user2FrozenAmount = account.stream().collect(Collectors.groupingBy(Account::getUserId, Collectors.summingInt(Account::getFrozenAmount)));
        var refund = refundLogService.lambdaQuery().in(RefundLog::getUserId, result.stream().map(CustomUserVo::getUserId).toList()).list();
        // refund按照用户维度计算退款次数和退款总金额
        var user2RefundAmount = refund.stream().collect(Collectors.groupingBy(RefundLog::getUserId, Collectors.summingInt(RefundLog::getRefund)));
        var user2RefundDiscountAmount = refund.stream().collect(Collectors.groupingBy(RefundLog::getUserId, Collectors.summingInt(RefundLog::getDiscountAmount)));
        var user2RefundTimes = refund.stream().collect(Collectors.groupingBy(RefundLog::getUserId, Collectors.counting()));

        // 将用户余额，退款次数，退款金额放入result中
        var res = result.stream().peek(vo -> {
            vo.setBalance(user2Balance.getOrDefault(vo.getUserId(), 0));
            vo.setFrozenAmount(user2FrozenAmount.getOrDefault(vo.getUserId(), 0));
            vo.setRefundTimes(user2RefundTimes.getOrDefault(vo.getUserId(), 0L));
            vo.setRefundAmount(user2RefundAmount.getOrDefault(vo.getUserId(), 0));
            vo.setRefundDiscountAmount(user2RefundDiscountAmount.getOrDefault(vo.getUserId(), 0));
        }).toList();
        page.setList(res);
        return page;
    }

    @Override
    public PageBean<User> listSimpleUser(CommonQueryParam params) {
        PageHelper.startPage(params.getPageNum(), params.getPageSize());
        var list = lambdaQuery()
                .select(User::getId, User::getMobilePhone, User::getNickname, User::getUsername)
                .like(!CommUtil.isEmptyOrNull(params.getMobilePhone()), User::getMobilePhone, params.getMobilePhone())
                .orderByDesc(User::getCreateTime)
                .list();
        return new PageBean<>(list);
    }


}
