package com.jeesharp.business.modules.member.application;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.jeesharp.business.modules.member.config.MemberIapProperties;
import com.jeesharp.business.modules.member.convert.MemberVipConvert;
import com.jeesharp.business.modules.member.huawei.HuaweiIapOrderRemoteClient;
import com.jeesharp.business.modules.member.huawei.HuaweiIapParsedPurchase;
import com.jeesharp.business.modules.member.huawei.HuaweiPurchaseVerificationResult;
import com.jeesharp.business.modules.member.dto.MemberVipSaveDTO;
import com.jeesharp.business.modules.member.dto.VipIapConfirmPurchaseDTO;
import com.jeesharp.business.modules.member.dto.VipIapCreateOrderDTO;
import com.jeesharp.business.modules.member.entity.AppIapOrder;
import com.jeesharp.business.modules.member.entity.Member;
import com.jeesharp.business.modules.member.entity.MemberLevel;
import com.jeesharp.business.modules.member.entity.MemberVip;
import com.jeesharp.business.modules.member.enums.IapFulfillStatusEnum;
import com.jeesharp.business.modules.member.enums.IapPayStatusEnum;
import com.jeesharp.business.modules.member.enums.IapRefundStatusEnum;
import com.jeesharp.business.modules.member.service.AppIapOrderService;
import com.jeesharp.business.modules.member.service.MemberLevelService;
import com.jeesharp.business.modules.member.service.MemberService;
import com.jeesharp.business.modules.member.service.MemberVipService;
import com.jeesharp.business.modules.member.vo.AppIapOrderQueryVO;
import com.jeesharp.business.modules.member.vo.VipIapOrderVO;
import com.jeesharp.framework.exception.BizErrorException;
import com.jeesharp.framework.pojo.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * 华为 IAP VIP 订单：创建、支付确认、发货（履约）、关键事件通知幂等处理。
 *
 * @author jeesharp
 */
@Slf4j
@Service
@Transactional
@RequiredArgsConstructor
public class MemberIapOrderApplicationService {

    private static final String DEFAULT_VIP_LEVEL = "VIP";

    private final AppIapOrderService appIapOrderService;
    private final MemberService memberService;
    private final MemberLevelService memberLevelService;
    private final MemberVipService memberVipService;
    private final MemberIapProperties memberIapProperties;
    private final HuaweiIapOrderRemoteClient huaweiIapOrderRemoteClient;

    private static final ZoneId ZONE_CN = ZoneId.of("Asia/Shanghai");

    /**
     * 创建待支付订单（客户端拉起华为收银台前调用）
     */
    @Transactional(readOnly = false)
    public VipIapOrderVO createOrder(String memberId, VipIapCreateOrderDTO dto) {
        Member member = memberService.get(memberId);
        if (member == null) {
            throw new BizErrorException("会员不存在");
        }
        Integer planId = dto.getPlanId();
        AppIapOrder order = new AppIapOrder();
        // id 仍用雪花；order_no 单独用 IAP+UUID(32)，避免与华为 purchaseOrderId 等纯数字串在 uk_iap_order_no 上撞号，也避免与历史 IAP+数字 数据冲突
        order.setId(IdUtil.getSnowflakeNextIdStr());
        order.setOrderNo("IAP" + UUID.randomUUID().toString().replace("-", ""));
        order.setMemberId(memberId);
        order.setPlanId(planId);
        applyPlanSku(order, planId);
        order.setLevelCode(DEFAULT_VIP_LEVEL);
        order.setPayStatus(IapPayStatusEnum.PENDING.getValue());
        order.setFulfillStatus(IapFulfillStatusEnum.PENDING.getValue());
        order.setRefundStatus(IapRefundStatusEnum.NONE.getValue());
        order.preInsert();
        order.setDelFlag("0");
        appIapOrderService.saveOrUpdate(order);
        return toVo(appIapOrderService.get(order.getId()));
    }

    /**
     * 客户端支付成功后回传华为订单信息，幂等发货。
     * <p>{@code skip-verify-purchase=false} 时先调华为 Order 服务验单再落库、履约。</p>
     */
    @Transactional(readOnly = false)
    public VipIapOrderVO confirmPurchase(String memberId, VipIapConfirmPurchaseDTO dto) {
        AppIapOrder order = appIapOrderService.getByOrderNo(dto.getOrderNo());
        if (order == null) {
            throw new BizErrorException("订单不存在");
        }
        if (!memberId.equals(order.getMemberId())) {
            throw new BizErrorException("无权操作该订单");
        }
        if (IapFulfillStatusEnum.FULFILLED.getValue().equals(order.getFulfillStatus())) {
            return toVo(order);
        }
        AppIapOrder tokenOwner = appIapOrderService.getByPurchaseToken(dto.getHuaweiPurchaseToken());
        if (tokenOwner != null && !tokenOwner.getId().equals(order.getId())) {
            throw new BizErrorException("purchaseToken 已绑定其他订单");
        }

        if (!memberIapProperties.isSkipVerifyPurchase()) {
            if (StrUtil.isBlank(order.getSkuProductId())) {
                throw new BizErrorException("订单缺少商品ID，无法验单");
            }
            HuaweiPurchaseVerificationResult vr = huaweiIapOrderRemoteClient.verifyPurchaseToken(
                    dto.getHuaweiPurchaseToken(), order.getSkuProductId());
            assertHuaweiPurchaseVerified(order, vr, dto.getHuaweiPurchaseToken());
            HuaweiIapParsedPurchase p = vr.getPurchase();
            order = appIapOrderService.get(order.getId());
            if (StrUtil.isNotBlank(dto.getHuaweiPurchaseOrderId())) {
                order.setHuaweiPurchaseOrderId(dto.getHuaweiPurchaseOrderId());
            }
            order.setHuaweiPurchaseToken(dto.getHuaweiPurchaseToken());
            if (StrUtil.isNotBlank(dto.getPurchasePayload())) {
                String payload = dto.getPurchasePayload();
                if (payload.length() > 4000) {
                    payload = payload.substring(0, 4000);
                }
                order.setPurchasePayload(payload);
            }
            applyHuaweiParsedToOrder(order, p, dto.getHuaweiPurchaseToken());
            order.preUpdate();
            appIapOrderService.saveOrUpdate(order);
            fulfillVip(appIapOrderService.get(order.getId()));
            afterFulfillMaybeConfirmHuawei(appIapOrderService.get(order.getId()), p, dto.getHuaweiPurchaseToken());
            return toVo(appIapOrderService.get(order.getId()));
        }

        order.setHuaweiPurchaseOrderId(dto.getHuaweiPurchaseOrderId());
        order.setHuaweiPurchaseToken(dto.getHuaweiPurchaseToken());
        if (StrUtil.isNotBlank(dto.getPurchasePayload())) {
            String payload = dto.getPurchasePayload();
            if (payload.length() > 4000) {
                payload = payload.substring(0, 4000);
            }
            order.setPurchasePayload(payload);
        }
        order.setPayStatus(IapPayStatusEnum.PAID.getValue());
        order.setPayTime(LocalDateTime.now());
        order.preUpdate();
        appIapOrderService.saveOrUpdate(order);
        fulfillVip(appIapOrderService.get(order.getId()));
        return toVo(appIapOrderService.get(order.getId()));
    }

    /**
     * 按业务订单号查询（当前会员）
     */
    @Transactional(readOnly = true)
    public VipIapOrderVO getOrderForMember(String memberId, String orderNo) {
        AppIapOrder order = appIapOrderService.getByOrderNo(orderNo);
        if (order == null || !memberId.equals(order.getMemberId())) {
            throw new BizErrorException("订单不存在");
        }
        return toVo(order);
    }

    /**
     * 当前会员 IAP 订单分页（按创建时间倒序）
     */
    @Transactional(readOnly = true)
    public Page<VipIapOrderVO> findOrderPage(String memberId, AppIapOrderQueryVO query) {
        AppIapOrderQueryVO q = query != null ? query : new AppIapOrderQueryVO();
        q.setMemberId(memberId);
        Page<AppIapOrder> page = new Page<>(q);
        Page<AppIapOrder> result = appIapOrderService.findPage(page, q);
        return (Page<VipIapOrderVO>) result.convert(this::toVo);
    }

    /**
     * 管理端：IAP 订单分页（不按当前登录会员过滤，按查询条件筛选，列表附带会员展示字段）。
     */
    @Transactional(readOnly = true)
    public Page<VipIapOrderVO> findAdminOrderPage(AppIapOrderQueryVO query) {
        AppIapOrderQueryVO q = query != null ? query : new AppIapOrderQueryVO();
        Page<AppIapOrder> page = new Page<>(q);
        Page<AppIapOrder> result = appIapOrderService.findPage(page, q);
        Map<String, Member> memberMap = new HashMap<>();
        for (AppIapOrder row : result.getRecords()) {
            String mid = row.getMemberId();
            if (StrUtil.isBlank(mid) || memberMap.containsKey(mid)) {
                continue;
            }
            memberMap.put(mid, memberService.get(mid));
        }
        return (Page<VipIapOrderVO>) result.convert(o -> toVoWithMember(o, memberMap.get(o.getMemberId())));
    }

    /**
     * 管理端：按主键查询详情（含会员信息、华为订单号等运维字段）。
     */
    @Transactional(readOnly = true)
    public VipIapOrderVO getAdminOrder(String id) {
        AppIapOrder order = appIapOrderService.get(id);
        if (order == null) {
            throw new BizErrorException("订单不存在");
        }
        Member m = StrUtil.isNotBlank(order.getMemberId()) ? memberService.get(order.getMemberId()) : null;
        VipIapOrderVO vo = toVoWithMember(order, m);
        vo.setLevelCode(order.getLevelCode());
        vo.setHuaweiPurchaseOrderId(order.getHuaweiPurchaseOrderId());
        return vo;
    }

    /**
     * 管理端：向华为 Order 服务验单并同步本地支付/履约状态（与「确认收货」共用华为验单链路）。
     */
    @Transactional(readOnly = false)
    public VipIapOrderVO adminSyncOrderStatus(String id) {
        return syncOrderFromHuaweiAndFulfill(id);
    }

    /**
     * 管理端：验单通过后执行虚拟商品履约（与「同步状态」一致，均走华为验单）。
     */
    @Transactional(readOnly = false)
    public VipIapOrderVO adminConfirmReceive(String id) {
        return syncOrderFromHuaweiAndFulfill(id);
    }

    /**
     * 调华为 /applications/purchases/tokens/verify，按返回更新订单并履约；消耗型可在履约后 confirm。
     */
    private VipIapOrderVO syncOrderFromHuaweiAndFulfill(String id) {
        AppIapOrder order = appIapOrderService.get(id);
        if (order == null) {
            throw new BizErrorException("订单不存在");
        }
        if (StrUtil.isBlank(order.getHuaweiPurchaseToken())) {
            throw new BizErrorException("订单缺少 purchaseToken，无法调用华为验单");
        }
        if (StrUtil.isBlank(order.getSkuProductId())) {
            throw new BizErrorException("订单缺少商品ID，无法验单");
        }
        HuaweiPurchaseVerificationResult vr = huaweiIapOrderRemoteClient.verifyPurchaseToken(
                order.getHuaweiPurchaseToken(), order.getSkuProductId());
        assertHuaweiPurchaseVerified(order, vr, order.getHuaweiPurchaseToken());
        HuaweiIapParsedPurchase p = vr.getPurchase();
        AppIapOrder fresh = appIapOrderService.get(id);
        applyHuaweiParsedToOrder(fresh, p, order.getHuaweiPurchaseToken());
        fresh.preUpdate();
        appIapOrderService.saveOrUpdate(fresh);
        fulfillVip(appIapOrderService.get(id));
        afterFulfillMaybeConfirmHuawei(appIapOrderService.get(id), p, order.getHuaweiPurchaseToken());
        return getAdminOrder(id);
    }

    private void assertHuaweiPurchaseVerified(AppIapOrder order, HuaweiPurchaseVerificationResult vr, String requestPurchaseToken) {
        if (vr == null || !vr.isSuccess()) {
            String msg = vr == null ? "无验单结果" : StrUtil.blankToDefault(vr.getResponseMessage(), "验单失败");
            String code = vr == null ? "" : StrUtil.blankToDefault(vr.getResponseCode(), "");
            throw new BizErrorException("华为验单失败[" + code + "]：" + msg);
        }
        HuaweiIapParsedPurchase p = vr.getPurchase();
        if (p == null) {
            throw new BizErrorException("华为验单返回数据异常");
        }
        if (p.getPurchaseState() == null || p.getPurchaseState() != 0) {
            throw new BizErrorException("华为侧订单状态非成功，purchaseState=" + p.getPurchaseState());
        }
        if (StrUtil.isNotBlank(p.getProductId()) && StrUtil.isNotBlank(order.getSkuProductId())
                && !StrUtil.equals(StrUtil.trim(p.getProductId()), StrUtil.trim(order.getSkuProductId()))) {
            throw new BizErrorException("验单商品与订单不一致");
        }
        Long expect = memberIapProperties.getExpectedApplicationId();
        if (expect != null && p.getApplicationId() != null && !expect.equals(p.getApplicationId())) {
            throw new BizErrorException("验单 applicationId 与配置不符");
        }
        if (StrUtil.isNotBlank(p.getPurchaseToken()) && StrUtil.isNotBlank(requestPurchaseToken)
                && !StrUtil.equals(p.getPurchaseToken(), requestPurchaseToken)) {
            throw new BizErrorException("验单 purchaseToken 与请求不一致");
        }
    }

    private void applyHuaweiParsedToOrder(AppIapOrder order, HuaweiIapParsedPurchase p, String purchaseTokenForStore) {
        if (StrUtil.isNotBlank(p.getOrderId())) {
            order.setHuaweiPurchaseOrderId(p.getOrderId());
        }
        if (StrUtil.isNotBlank(purchaseTokenForStore)) {
            order.setHuaweiPurchaseToken(purchaseTokenForStore);
        }
        order.setPayStatus(IapPayStatusEnum.PAID.getValue());
        if (p.getPurchaseTimeMillis() != null) {
            order.setPayTime(LocalDateTime.ofInstant(Instant.ofEpochMilli(p.getPurchaseTimeMillis()), ZONE_CN));
        } else {
            order.setPayTime(LocalDateTime.now());
        }
    }

    private void afterFulfillMaybeConfirmHuawei(AppIapOrder latest, HuaweiIapParsedPurchase p, String purchaseToken) {
        if (!memberIapProperties.isConfirmAfterFulfill()) {
            return;
        }
        Integer kind = p.getKind();
        if (kind != null && kind != 0) {
            return;
        }
        if (StrUtil.isBlank(purchaseToken) || StrUtil.isBlank(latest.getSkuProductId())) {
            return;
        }
        huaweiIapOrderRemoteClient.confirmPurchaseQuietly(purchaseToken, latest.getSkuProductId());
    }

    /**
     * 华为关键事件通知：按 purchaseOrderId / purchaseToken 匹配业务订单并幂等发货
     */
    @Transactional(readOnly = false)
    public void handleHuaweiKeyEvent(String purchaseOrderId, String purchaseToken, String productId) {
        if (StrUtil.isBlank(purchaseOrderId) && StrUtil.isBlank(purchaseToken)) {
            return;
        }
        AppIapOrder order = null;
        if (StrUtil.isNotBlank(purchaseOrderId)) {
            order = appIapOrderService.getByHuaweiPurchaseOrderId(purchaseOrderId);
        }
        if (order == null && StrUtil.isNotBlank(purchaseToken)) {
            order = appIapOrderService.getByPurchaseToken(purchaseToken);
        }
        if (order == null) {
            log.warn("IAP notify: 未匹配业务订单 purchaseOrderId={} productId={}", purchaseOrderId, productId);
            return;
        }
        if (IapFulfillStatusEnum.FULFILLED.getValue().equals(order.getFulfillStatus())) {
            return;
        }
        if (StrUtil.isNotBlank(purchaseToken)) {
            order.setHuaweiPurchaseToken(purchaseToken);
        }
        if (StrUtil.isNotBlank(purchaseOrderId)) {
            order.setHuaweiPurchaseOrderId(purchaseOrderId);
        }
        if (!IapPayStatusEnum.PAID.getValue().equals(order.getPayStatus())) {
            order.setPayStatus(IapPayStatusEnum.PAID.getValue());
            order.setPayTime(LocalDateTime.now());
        }
        order.preUpdate();
        appIapOrderService.saveOrUpdate(order);
        fulfillVip(appIapOrderService.get(order.getId()));
    }

    private void applyPlanSku(AppIapOrder order, Integer planId) {
        if (planId == null) {
            throw new BizErrorException("套餐无效");
        }
        if (planId == 0) {
            order.setSkuProductId("4");
            order.setSkuName("月卡");
        } else if (planId == 1) {
            order.setSkuProductId("5");
            order.setSkuName("季卡");
        } else if (planId == 2) {
            order.setSkuProductId("6");
            order.setSkuName("年卡");
        } else {
            throw new BizErrorException("套餐无效");
        }
    }

    private int resolveMonths(Integer planId) {
        if (planId == null) {
            return 0;
        }
        if (planId == 0) {
            return 1;
        }
        if (planId == 1) {
            return 3;
        }
        return 12;
    }

    private void fulfillVip(AppIapOrder order) {
        if (order == null) {
            return;
        }
        if (IapFulfillStatusEnum.FULFILLED.getValue().equals(order.getFulfillStatus())) {
            return;
        }
        if (!IapPayStatusEnum.PAID.getValue().equals(order.getPayStatus())) {
            return;
        }
        Member member = memberService.get(order.getMemberId());
        if (member == null) {
            log.error("IAP fulfill: 会员不存在 orderNo={}", order.getOrderNo());
            order.setFulfillStatus(IapFulfillStatusEnum.FAILED.getValue());
            order.preUpdate();
            appIapOrderService.saveOrUpdate(order);
            return;
        }
        MemberLevel level = memberLevelService.getByLevelCode(order.getLevelCode());
        if (level == null || level.getIsPaidLevel() == null || level.getIsPaidLevel() != 1) {
            log.error("IAP fulfill: 付费等级无效 levelCode={} orderNo={}", order.getLevelCode(), order.getOrderNo());
            order.setFulfillStatus(IapFulfillStatusEnum.FAILED.getValue());
            order.preUpdate();
            appIapOrderService.saveOrUpdate(order);
            return;
        }
        LocalDateTime now = LocalDateTime.now();
        MemberVip active = memberVipService.getActiveVip(order.getMemberId());
        LocalDateTime base = now;
        if (active != null && active.getExpireDate() != null && active.getExpireDate().isAfter(now)) {
            base = active.getExpireDate();
        }
        int months = resolveMonths(order.getPlanId());
        if (months <= 0) {
            order.setFulfillStatus(IapFulfillStatusEnum.FAILED.getValue());
            order.preUpdate();
            appIapOrderService.saveOrUpdate(order);
            return;
        }
        LocalDateTime expire = base.plusMonths(months);
        MemberVipSaveDTO saveDto = new MemberVipSaveDTO();
        saveDto.setMemberId(order.getMemberId());
        saveDto.setLevelCode(order.getLevelCode());
        saveDto.setProductId(order.getSkuProductId());
        saveDto.setProductName(order.getSkuName());
        saveDto.setOrderNo(order.getOrderNo());
        saveDto.setStartDate(now);
        saveDto.setExpireDate(expire);
        MemberVip entity = MemberVipConvert.INSTANCE.fromSaveDTO(saveDto);
        MemberVip saved = memberVipService.openVip(entity);
        order.setMemberVipId(saved.getId());
        order.setFulfillStatus(IapFulfillStatusEnum.FULFILLED.getValue());
        order.setFulfillTime(LocalDateTime.now());
        order.preUpdate();
        appIapOrderService.saveOrUpdate(order);
    }

    private VipIapOrderVO toVoWithMember(AppIapOrder o, Member m) {
        VipIapOrderVO vo = toVo(o);
        if (vo == null) {
            return null;
        }
        vo.setMemberId(o.getMemberId());
        if (m != null) {
            vo.setMemberNo(m.getMemberNo());
            vo.setNickname(m.getNickname());
            vo.setMobile(m.getMobile());
        }
        return vo;
    }

    private VipIapOrderVO toVo(AppIapOrder o) {
        if (o == null) {
            return null;
        }
        VipIapOrderVO vo = new VipIapOrderVO();
        vo.setId(o.getId());
        vo.setOrderNo(o.getOrderNo());
        vo.setPlanId(o.getPlanId());
        vo.setSkuProductId(o.getSkuProductId());
        vo.setSkuName(o.getSkuName());
        vo.setPayAmount(resolvePayAmountYuan(o.getPlanId()));
        vo.setCreateDate(o.getCreateDate());
        vo.setPayStatus(o.getPayStatus());
        vo.setPayStatusName(IapPayStatusEnum.getDescByValue(o.getPayStatus()));
        vo.setFulfillStatus(o.getFulfillStatus());
        vo.setFulfillStatusName(IapFulfillStatusEnum.getDescByValue(o.getFulfillStatus()));
        vo.setRefundStatus(o.getRefundStatus());
        vo.setRefundStatusName(IapRefundStatusEnum.getDescByValue(o.getRefundStatus()));
        vo.setPayTime(o.getPayTime());
        vo.setFulfillTime(o.getFulfillTime());
        vo.setMemberVipId(o.getMemberVipId());
        return vo;
    }

    /**
     * 套餐定价（元），与客户端 VIP 续费页、createOrder 套餐一致
     */
    private BigDecimal resolvePayAmountYuan(Integer planId) {
        if (planId == null) {
            return null;
        }
        if (planId == 0) {
            return new BigDecimal("5.00");
        }
        if (planId == 1) {
            return new BigDecimal("12.00");
        }
        if (planId == 2) {
            return new BigDecimal("48.00");
        }
        return null;
    }
}
