package com.kym.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.common.utils.IDGenerator;
import com.kym.entity.*;
import com.kym.entity.common.PageBean;
import com.kym.entity.queryParams.CommonQueryParam;
import com.kym.entity.queryParams.WxLoginParams;
import com.kym.entity.vo.CustomUserVo;
import com.kym.entity.vo.UserVo;
import com.kym.entity.wechat.WxPhoneNum;
import com.kym.mapper.MpRelationMapper;
import com.kym.mapper.UserMapper;
import com.kym.service.AccountService;
import com.kym.service.CarsService;
import com.kym.service.RefundLogService;
import com.kym.service.UserService;
import com.kym.service.cache.KymCache;
import jakarta.annotation.PostConstruct;
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.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.temporal.TemporalAdjusters;
import java.util.Arrays;
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
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 MpRelationMapper mpRelationMapper;


    public UserServiceImpl(WxConfig wxConfig, @Lazy AccountService accountService, RefundLogService refundLogService,
                           CarsService carsService, MpRelationMapper mpRelationMapper) {
        this.wxConfig = wxConfig;
        this.accountService = accountService;
        this.refundLogService = refundLogService;
        this.carsService = carsService;
        this.mpRelationMapper = mpRelationMapper;
    }

    @PostConstruct
    private void init() {
        // 初始化缓存
        QueryWrapper<User> wrapper = new QueryWrapper<User>().select("id", "station_id");
        List<User> users = baseMapper.selectList(wrapper);
        var map = users.stream()
                .filter(u -> CommUtil.isNotEmptyAndNull(u.getStationId()))
                .collect(Collectors.toMap(User::getId, User::getStationId));
        KymCache.INSTANCE.putUserId2StationId(map);
    }


    @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 {
                if (CommUtil.isEmptyOrNull(params.getPhoneCode())) {
                    throw new BusinessException(ResponseEnum.WX_MP_LOGIN_ERROR);
                }
                // 注册逻辑
                // 新增用户
                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());
                // 用户归属站点：扫码设备注册的直接确定，自主注册的在首次消费时自动归属
                if (CommUtil.isNotEmptyAndNull(params.getShortId())) {
                    newUser.setStationId(KymCache.INSTANCE.gesStationIdByShortId(params.getShortId()));
                }
                baseMapper.insert(newUser);
                long userId = newUser.getId();
                if (CommUtil.isNotEmptyAndNull(newUser.getStationId())) {
                    KymCache.INSTANCE.putUserId2StationId(Map.of(userId, newUser.getStationId()));
                }
                // 创建用户账户
                var account = new Account();
                account.setUserId(userId);
                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 cars = carsService.lambdaQuery()
                .eq(Cars::getUserId, userId)
                .eq(Cars::getIsDefault, 1)
                .list();
        var car = cars.stream().findFirst().orElse(null);
        if (cars.size() > 1) {
            // 清理历史脏数据：仅保留第一个默认车辆，其余清除默认标记
            var staleDefaults = cars.stream().skip(1).peek(c -> c.setIsDefault(false)).toList();
            carsService.updateBatchById(staleDefaults);
        }
        if (car != null) {
            userVo.setDefaultPlateNo(car.getPlateNo());
            userVo.setVin(car.getVin());
        }
        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());
        if (user.getStationId() != null) {
            StpUtil.getSession().set("stationId", user.getStationId());
        }
        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()));
    }

    protected void updateUnionid() {
        var unionid = StpUtil.getSession().getString("unionid");
        if (CommUtil.isNotEmptyAndNull(unionid)) {
            // 查询现有关注状态
            var mpRelation = mpRelationMapper.selectOne(
                    new QueryWrapper<MpRelation>().eq("unionid", 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);
            // 同步关注状态到 t_user
            if (mpRelation != null && mpRelation.getSubscribe() != null) {
                Integer followStatus = mpRelation.getSubscribe() ? 1 : 2;
                lambdaUpdate()
                        .set(User::getFollowOfficialAccount, followStatus)
                        .eq(User::getId, StpUtil.getLoginIdAsLong())
                        .update();
            }
        }
    }


    @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);
            car.setIsDelete(false);
            if (userVo.getVin() != null) {
                car.setVin(userVo.getVin());
            }
            // 将用户名下其他车辆设为非默认
            var cars = carsService.lambdaQuery()
                    .eq(Cars::getUserId, userVo.getId())
                    .and(w -> w.eq(Cars::getIsDelete, false).or().isNull(Cars::getIsDelete))
                    .list();
            cars.stream()
                    .filter(c -> !userVo.getDefaultPlateNo().equals(c.getPlateNo()))
                    .forEach(s -> s.setIsDefault(false));
            carsService.updateBatchById(cars);
            var wrapper = new QueryWrapper<Cars>();
            wrapper.eq("plate_no", userVo.getDefaultPlateNo());
            carsService.saveOrUpdate(car, wrapper);
        }
    }

    @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) {
        // 站点数据权限
        var adminStationIds = KymCache.INSTANCE.getAdminUserStationIds(StpUtil.getLoginIdAsLong());
        if (CommUtil.isEmptyOrNull(params.getStationId()) &&
                CommUtil.isNotEmptyAndNull(KymCache.INSTANCE.getAdminUserStationIds(StpUtil.getLoginIdAsLong()))) {
            params.setStationId(adminStationIds.get(0));
        }
        List<Long> userIds = lambdaQuery()
                .eq(CommUtil.isNotEmptyAndNull(params.getMobilePhone()), User::getMobilePhone, params.getMobilePhone())
                .eq(CommUtil.isNotEmptyAndNull(params.getStationId()), User::getStationId, params.getStationId())
                .eq(CommUtil.isNotEmptyAndNull(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 user2rechargeBalance = account.stream().collect(Collectors.groupingBy(Account::getUserId, Collectors.summingInt(Account::getRechargeBalance)));
        var user2GrantsBalance= account.stream().collect(Collectors.groupingBy(Account::getUserId, Collectors.summingInt(Account::getGrantsBalance)));
        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.setRechargeBalance(user2rechargeBalance.getOrDefault(vo.getUserId(), 0));
            vo.setGrantsBalance(user2GrantsBalance.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));
            vo.setStationName(KymCache.INSTANCE.getStationNameById(vo.getStationId()));
        }).toList();
        page.setList(res);
        return page;
    }


    /**
     * 统计指定日期注册用户数量
     *
     * @param statDay
     * @return
     */
    @Override
    public Map<String, Integer> countDailyRegister(LocalDate statDay, String... stationId) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.ge(User::getCreateTime, LocalDateTime.of(statDay, LocalTime.MIN));
        wrapper.lt(User::getCreateTime, LocalDateTime.of(statDay.plusDays(1), LocalTime.MIN));
        if (stationId.length > 0) {
            wrapper.in(User::getStationId, Arrays.asList(stationId));
        }
        // 按照站点分组统计订单数量
        return list(wrapper).stream().collect(Collectors.groupingBy(u -> u.getStationId() != null ? u.getStationId() : "未分配", Collectors.summingInt(o -> 1)));
    }

    /**
     * 统计指定日期当月注册用户数量
     *
     * @param statDay
     * @return
     */
    @Override
    public Map<String, Integer> countMonthRegister(LocalDate statDay, String... stationId) {
        var startTime = statDay.with(TemporalAdjusters.firstDayOfMonth()).atTime(LocalTime.MIN);
        var endTime = statDay.with(TemporalAdjusters.firstDayOfMonth()).plusMonths(1).atTime(LocalTime.MIN);
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.ge(User::getCreateTime, startTime);
        wrapper.lt(User::getCreateTime, endTime);
        if (stationId.length > 0) {
            wrapper.in(User::getStationId, Arrays.asList(stationId));
        }
        return list(wrapper).stream().collect(Collectors.groupingBy(u -> u.getStationId() != null ? u.getStationId() : "未分配", Collectors.summingInt(o -> 1)));
    }


}
