package edu.whut.infrastructure.adapter.repository;

import com.alibaba.fastjson2.JSON;
import edu.whut.domain.trade.adapter.repository.ITradeRepository;
import edu.whut.domain.trade.model.aggregate.GroupBuyOrderAggregate;
import edu.whut.domain.trade.model.aggregate.GroupBuyTeamSettlementAggregate;
import edu.whut.domain.trade.model.entity.*;
import edu.whut.domain.trade.model.valobj.GroupBuyProgressVO;
import edu.whut.domain.trade.model.valobj.TradeOrderStatusEnumVO;
import edu.whut.infrastructure.dao.IGroupBuyActivityDao;
import edu.whut.infrastructure.dao.IGroupBuyOrderDao;
import edu.whut.infrastructure.dao.IGroupBuyOrderListDao;
import edu.whut.infrastructure.dao.INotifyTaskDao;
import edu.whut.infrastructure.dao.po.GroupBuyActivity;
import edu.whut.infrastructure.dao.po.GroupBuyOrder;
import edu.whut.infrastructure.dao.po.GroupBuyOrderList;
import edu.whut.infrastructure.dao.po.NotifyTask;
import edu.whut.infrastructure.dcc.DCCService;
import edu.whut.types.common.Constants;
import edu.whut.types.enums.ActivityStatusEnumVO;
import edu.whut.types.enums.GroupBuyOrderStatusEnumVO;
import edu.whut.types.enums.ResponseCode;
import edu.whut.types.exception.AppException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;


/**
 * 仓储实现：负责把领域对象 <-> 数据库 PO 的转换与持久化；
 * 不做任何业务规则判断，只保证数据一致性。
 */
@Slf4j
@Repository
@RequiredArgsConstructor
public class TradeRepository implements ITradeRepository {

    private final IGroupBuyOrderDao groupBuyOrderDao;

    private final IGroupBuyOrderListDao groupBuyOrderListDao;

    private final IGroupBuyActivityDao groupBuyActivityDao;

    private final INotifyTaskDao notifyTaskDao;

    private final DCCService dccService;

    /**
     * 根据外部交易号 & 用户id 查询未支付的锁单记录（用于幂等）
     */
    @Override
    public MarketPayOrderEntity queryMarketPayOrderEntityByOutTradeNo(String userId, String outTradeNo) {
        // 只拼装查询条件需要的字段
        GroupBuyOrderList query = new GroupBuyOrderList();
        query.setUserId(userId);
        query.setOutTradeNo(outTradeNo);

        GroupBuyOrderList res = groupBuyOrderListDao.queryGroupBuyOrderRecordByOutTradeNo(query);
        if (res == null) {
            return null;
        }

        // 组装领域对象返回，只返回上层真正关心的属性
        return MarketPayOrderEntity.builder()
                .teamId(res.getTeamId())
                .orderId(res.getOrderId())
                .originalPrice(res.getOriginalPrice())
                .deductionPrice(res.getDeductionPrice())
                .payPrice(res.getPayPrice())
                .tradeOrderStatusEnumVO(TradeOrderStatusEnumVO.valueOf(res.getStatus()))
                .build();
    }

    /**
     * 锁定营销预支付订单<br>
     * - 新团：自己创建团，写入 group_buy_order，再写入 group_buy_order_list<br>
     * - 老团：更新 lock_count，满员时抛异常<br>
     * 事务超时 500 ms，成功后返回锁定好的订单信息
     */
    @Transactional(timeout = 500)
    @Override
    public MarketPayOrderEntity lockMarketPayOrder(GroupBuyOrderAggregate agg) {

        // 从聚合中拆出各子实体（聚合内已校验业务规则）
        UserEntity        user     = agg.getUserEntity();
        PayActivityEntity activity = agg.getPayActivityEntity();
        PayDiscountEntity discount = agg.getPayDiscountEntity();
        Integer userTakeOrderCount = agg.getUserTakeOrderCount();


        /* ---------- 1. 处理 group_buy_order（团单主表） ---------- */
        String teamId = activity.getTeamId();
        //自己发起拼团
        if (StringUtils.isBlank(teamId)) {
            // 新建团队，随机 8 位数字作 teamId（示例中用 RandomStringUtils，线上可换雪花算法等）
            teamId = RandomStringUtils.randomNumeric(8);
            // 日期处理
            Date currentDate = new Date();
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(currentDate);
            calendar.add(Calendar.MINUTE, activity.getValidTime());

            // 构建拼团订单
            GroupBuyOrder orderPo = GroupBuyOrder.builder()
                    .teamId(teamId)
                    .activityId(activity.getActivityId())
                    .source(discount.getSource())
                    .channel(discount.getChannel())
                    .originalPrice(discount.getOriginalPrice())
                    .deductionPrice(discount.getDeductionPrice())
                    .payPrice(discount.getOriginalPrice().subtract(discount.getDeductionPrice()))
                    .targetCount(activity.getTargetCount())
                    .completeCount(0)
                    .lockCount(1)  // 首单已锁定
                    .validStartTime(currentDate)
                    .validEndTime(calendar.getTime())
                    .notifyUrl(discount.getNotifyUrl())
                    .build();

            groupBuyOrderDao.insert(orderPo);
        } else {
            // 参加已有团队，先尝试把 lockCount +1 返回的是满足条件的记录个数。
            int rows = groupBuyOrderDao.updateAddLockCount(teamId);
            if (rows != 1) {
                // 返回特定业务异常：拼团已满员
                throw new AppException(ResponseCode.E0005);
            }
        }

        /* ---------- 2. 写入 group_buy_order_list（团单明细表） ---------- */
        String orderId = RandomStringUtils.randomNumeric(12); // 订单号

        //构建拼团明细单
        GroupBuyOrderList orderListPo = GroupBuyOrderList.builder()
                .userId(user.getUserId())
                .teamId(teamId)
                .orderId(orderId)
                .activityId(activity.getActivityId())
                .startTime(activity.getStartTime())
                .endTime(activity.getEndTime())
                .goodsId(discount.getGoodsId())
                .source(discount.getSource())
                .channel(discount.getChannel())
                .originalPrice(discount.getOriginalPrice())
                .deductionPrice(discount.getDeductionPrice())
                .payPrice(discount.getPayPrice())
                .status(TradeOrderStatusEnumVO.CREATE.getCode()) // 0 = 初始锁定
                .outTradeNo(discount.getOutTradeNo())
                // 构建 bizId 唯一值；活动id_用户id_参与次数累加
                .bizId(activity.getActivityId() + Constants.UNDERLINE + user.getUserId() + Constants.UNDERLINE + (userTakeOrderCount + 1))
                .build();

        try {
            groupBuyOrderListDao.insert(orderListPo);
        } catch (DuplicateKeyException e) {
            // 幂等冲突：同一 outTradeNo 已存在
            throw new AppException(ResponseCode.INDEX_EXCEPTION);
        }

        /* ---------- 3. 返回领域对象给上层 ---------- */
        return MarketPayOrderEntity.builder()
                .orderId(orderId)
                .originalPrice(discount.getOriginalPrice())
                .deductionPrice(discount.getDeductionPrice())
                .payPrice(discount.getPayPrice())
                .tradeOrderStatusEnumVO(TradeOrderStatusEnumVO.CREATE)
                .build();
    }

    /**
     * 查询拼团进度（当前已完成、目标、已锁定人数）
     */
    @Override
    public GroupBuyProgressVO queryGroupBuyProgress(String teamId) {
        GroupBuyOrder po = groupBuyOrderDao.queryGroupBuyProgress(teamId);
        if (po == null) {
            return null;
        }
        return GroupBuyProgressVO.builder()
                .completeCount(po.getCompleteCount())
                .targetCount(po.getTargetCount())
                .lockCount(po.getLockCount())
                .build();
    }

    /**
     * 根据 activityId 查询拼团活动实体。
     */
    @Override
    public GroupBuyActivityEntity queryGroupBuyActivityEntityByActivityId(Long activityId) {
        GroupBuyActivity groupBuyActivity = groupBuyActivityDao.queryGroupBuyActivityByActivityId(activityId);
        GroupBuyActivityEntity groupBuyActivityEntity = new GroupBuyActivityEntity();
        BeanUtils.copyProperties(groupBuyActivity, groupBuyActivityEntity);
        groupBuyActivityEntity.setStatus(
                ActivityStatusEnumVO.valueOf(groupBuyActivity.getStatus())
        );
        return groupBuyActivityEntity;
    }

    /**
     * 查询用户在某活动中已下单次数（用于限购校验）。
     */
    @Override
    public Integer queryOrderCountByActivityId(Long activityId, String userId) {
        GroupBuyOrderList groupBuyOrderListReq = new GroupBuyOrderList();
        groupBuyOrderListReq.setActivityId(activityId);
        groupBuyOrderListReq.setUserId(userId);
        return groupBuyOrderListDao.queryOrderCountByActivityId(groupBuyOrderListReq);
    }

    /**
     * 查询团队订单（主表）信息。
     */
    @Override
    public GroupBuyTeamEntity queryGroupBuyTeamByTeamId(String teamId) {
        GroupBuyOrder groupBuyOrder = groupBuyOrderDao.queryGroupBuyTeamByTeamId(teamId);
        GroupBuyTeamEntity groupBuyTeamEntity = new GroupBuyTeamEntity();
        BeanUtils.copyProperties(groupBuyOrder, groupBuyTeamEntity, "status");
        groupBuyTeamEntity.setStatus(GroupBuyOrderStatusEnumVO.valueOf(groupBuyOrder.getStatus()));
        return groupBuyTeamEntity;
    }


    /**
     * 拼团支付成功后的结算逻辑
     * 步骤：
     *   更新明细表状态 → COMPLETE
     *   更新主表 complete_count + 1
     *   当达到 targetCount 时，更新主表状态 → COMPLETE 并写入回调任务
     * 整体包裹在 500ms 事务内，任何一步失败都会回滚。
     */
    @Transactional(timeout = 500)
    @Override
    public boolean settlementMarketPayOrder(GroupBuyTeamSettlementAggregate agg) {

        // ========= 1. 聚合拆分 =========
        UserEntity           user   = agg.getUserEntity();
        GroupBuyTeamEntity   team   = agg.getGroupBuyTeamEntity();
        TradePaySuccessEntity payOK = agg.getTradePaySuccessEntity();

        // ========= 2. 更新拼团订单明细状态 =========
        GroupBuyOrderList req = new GroupBuyOrderList();
        req.setUserId(user.getUserId());
        req.setOutTradeNo(payOK.getOutTradeNo());
        req.setOutTradeTime(payOK.getOutTradeTime());

        //更新订单状态到已完成
        int rows = groupBuyOrderListDao.updateOrderStatus2COMPLETE(req);
        if (rows != 1) {
            throw new AppException(ResponseCode.UPDATE_ZERO);
        }

        // ========= 3. 更新拼团已完成人数 =========
        int addCnt = groupBuyOrderDao.updateAddCompleteCount(team.getTeamId());
        if (addCnt != 1) {
            throw new AppException(ResponseCode.UPDATE_ZERO);
        }

        // ========= 4. 达标后收尾（状态改为 COMPLETE & 写入回调任务） =========
        if (team.getTargetCount() - team.getCompleteCount() == 1) { // 刚好凑齐
            int updStat = groupBuyOrderDao.updateOrderStatus2COMPLETE(team.getTeamId());
            if (updStat != 1) {
                throw new AppException(ResponseCode.UPDATE_ZERO);
            }

            // 查询已完成外部交易号列表，用于业务通知
            List<String> outTradeNoList = groupBuyOrderListDao.queryGroupBuyCompleteOrderOutTradeNoListByTeamId(team.getTeamId());

            // 构造并写入通知任务（异步回调）
            NotifyTask task = new NotifyTask();
            task.setActivityId(team.getActivityId());
            task.setTeamId(team.getTeamId());
            task.setNotifyUrl(team.getNotifyUrl());
            task.setNotifyCount(0);
            task.setNotifyStatus(0);
            task.setParameterJson(JSON.toJSONString(new HashMap<String, Object>() {{
                put("teamId", team.getTeamId());
                put("outTradeNoList", outTradeNoList);
            }}));

            notifyTaskDao.insert(task);
            return true;
        }
        return false;
    }

    /**
     * 渠道是否被拦截
     * @param source
     * @param channel
     * @return
     */
    @Override
    public boolean isSCBlackIntercept(String source, String channel) {
        return dccService.isSCBlackIntercept(source, channel);
    }

    /**
     * 查询 所有 未执行或待重试的通知任务
     * @return 如果不存在相关任务，则返回空列表；否则返回实体列表
     */
    @Override
    public List<NotifyTaskEntity> queryUnExecutedNotifyTaskList() {
        List<NotifyTask> notifyTaskList = notifyTaskDao.queryUnExecutedNotifyTaskList();
        if (CollectionUtils.isEmpty(notifyTaskList)) {
            return Collections.emptyList();
        }

        List<NotifyTaskEntity> notifyTaskEntities = new ArrayList<>();
        for (NotifyTask notifyTask : notifyTaskList) {

            NotifyTaskEntity notifyTaskEntity = new NotifyTaskEntity();
            // 将同名属性从 task 拷贝到 entity
            BeanUtils.copyProperties(notifyTask, notifyTaskEntity);
            notifyTaskEntities.add(notifyTaskEntity);
        }

        return notifyTaskEntities;
    }

    /**
     * 根据指定 teamId 查询未执行或待重试的单个通知任务
     * @param teamId 拼团团队标识
     * @return 返回长度为 0 或 1 的列表，封装 NotifyTaskEntity
     */
    @Override
    public List<NotifyTaskEntity> queryUnExecutedNotifyTaskList(String teamId) {
        NotifyTask notifyTask = notifyTaskDao.queryUnExecutedNotifyTaskByTeamId(teamId);
        if (null == notifyTask) return new ArrayList<>();
        return Collections.singletonList(NotifyTaskEntity.builder()
                .teamId(notifyTask.getTeamId())
                .notifyUrl(notifyTask.getNotifyUrl())
                .notifyCount(notifyTask.getNotifyCount())
                .parameterJson(notifyTask.getParameterJson())
                .build());
    }

    /**
     * 更新指定 teamId 的通知任务状态为成功
     */
    @Override
    public int updateNotifyTaskStatusSuccess(String teamId) {
        return notifyTaskDao.updateNotifyTaskStatusSuccess(teamId);
    }

    /**
     * 更新指定 teamId 的通知任务状态为失败（不可重试）
     */
    @Override
    public int updateNotifyTaskStatusError(String teamId) {
        return notifyTaskDao.updateNotifyTaskStatusError(teamId);
    }

    /**
     * 更新指定 teamId 的通知任务状态为重试
     */
    @Override
    public int updateNotifyTaskStatusRetry(String teamId) {
        return notifyTaskDao.updateNotifyTaskStatusRetry(teamId);
    }
}
