package com.jeesharp.business.modules.finance.paymentrecord.application;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.jeesharp.business.modules.finance.paymentrecord.convert.PayRecordConvert;
import com.jeesharp.business.modules.finance.paymentrecord.dto.PayRecordSaveDTO;
import com.jeesharp.business.modules.finance.paymentrecord.entity.PayRecord;
import com.jeesharp.business.modules.finance.paymentrecord.enums.PayRecordBizKindEnum;
import com.jeesharp.business.modules.finance.paymentrecord.enums.PayRecordBizSceneEnum;
import com.jeesharp.business.modules.finance.paymentrecord.enums.PayRecordCurrencyEnum;
import com.jeesharp.business.modules.finance.paymentrecord.enums.PayRecordPayTypeEnum;
import com.jeesharp.business.modules.finance.paymentrecord.enums.PayRecordStatusEnum;
import com.jeesharp.business.modules.finance.paymentrecord.service.PayRecordService;
import com.jeesharp.business.modules.finance.paymentrecord.vo.PayRecordInfoVO;
import com.jeesharp.business.modules.finance.paymentrecord.vo.PayRecordListVO;
import com.jeesharp.business.modules.finance.paymentrecord.vo.PayRecordQueryVO;
import com.jeesharp.business.modules.member.service.MemberService;
import com.jeesharp.framework.pojo.Page;
import com.jeesharp.framework.pojo.query.PageRespVO;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import com.jeesharp.business.modules.finance.api.support.WechatPayNotifyParseSupport;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * 支付记录应用服务：管理端查询、展示字段字典翻译与保存默认值编排。
 *
 * @author jeesharp
 * @since 2026-05-09
 */
@Slf4j
@Service
@AllArgsConstructor
@Transactional(readOnly = true)
public class PayRecordApplicationService {

    private final PayRecordService payRecordService;
    private final MemberService memberService;

    public PayRecordInfoVO get(String id) {
        PayRecordInfoVO vo = PayRecordConvert.INSTANCE.toInfoVO(payRecordService.get(id));
        fillInfoNames(vo);
        memberService.enrichMemberDisplay(vo);
        return vo;
    }

    public PageRespVO<PayRecordListVO> findPage(Page<PayRecord> page, PayRecordQueryVO query) {
        PayRecordQueryVO prepared = prepareQuery(query);
        if (prepared == null) {
            return emptyPage(page);
        }
        Page<PayRecord> result = payRecordService.findPage(page, prepared);
        PageRespVO<PayRecordListVO> pageVo = PayRecordConvert.INSTANCE.toPageRespVO(result);
        if (pageVo.getRecords() != null) {
            pageVo.getRecords().forEach(this::fillListNames);
        }
        memberService.enrichMemberDisplay(pageVo.getRecords());
        return pageVo;
    }

    @Transactional(readOnly = false)
    public void save(PayRecordSaveDTO dto) {
        PayRecord entity = PayRecordConvert.INSTANCE.toEntity(dto);
        if (dto.getBizKind() == null) {
            entity.setBizKind(PayRecordBizKindEnum.PAY.getValue());
        }
        if (dto.getPayStatus() == null) {
            entity.setPayStatus(PayRecordStatusEnum.PENDING.getValue());
        }
        if (StrUtil.isBlank(entity.getCurrency())) {
            entity.setCurrency(PayRecordCurrencyEnum.CNY.getCode());
        }
        payRecordService.save(entity);
    }

    /**
     * 微信支付 JSAPI/小程序回调成功后落库一条支付流水；以微信 {@code transaction_id} 为主键幂等，避免重复回调重复插入。
     *
     * @param bizSceneCode    {@link PayRecordBizSceneEnum} 场景编码
     * @param bizOrderId      业务主单 ID（可为空则仅依赖商户单号留痕）
     * @param merchantOrderNo 商户订单号（微信 out_trade_no）
     * @param thirdPartyTradeNo 微信 transaction_id
     */
    @Transactional(readOnly = false)
    public void recordWechatJsapiNotifyIfAbsent(String bizSceneCode, String bizOrderId, String merchantOrderNo,
            String thirdPartyTradeNo, BigDecimal payAmountYuan, String subject, String memberId,
            LocalDateTime paySuccessTime, String rawCallbackBody) {
        if (StrUtil.isNotBlank(thirdPartyTradeNo)) {
            PayRecordQueryVO q = new PayRecordQueryVO();
            q.setPayOrderNo(thirdPartyTradeNo);
            List<PayRecord> existed = payRecordService.findList(q);
            if (existed != null && !existed.isEmpty()) {
                return;
            }
        } else if (StrUtil.isNotBlank(merchantOrderNo)) {
            PayRecordQueryVO q = new PayRecordQueryVO();
            q.setMerchantOrderNo(merchantOrderNo);
            q.setPayStatus(PayRecordStatusEnum.SUCCESS.getValue());
            List<PayRecord> existed = payRecordService.findList(q);
            if (existed != null && !existed.isEmpty()) {
                return;
            }
        } else {
            log.warn("recordWechatJsapiNotifyIfAbsent skipped: no transaction_id and no out_trade_no");
            return;
        }

        PayRecordSaveDTO dto = new PayRecordSaveDTO();
        dto.setBizScene(bizSceneCode);
        dto.setBizId(StrUtil.isBlank(bizOrderId) ? null : bizOrderId);
        dto.setBizType("ORDER");
        dto.setMerchantOrderNo(merchantOrderNo);
        dto.setPayOrderNo(StrUtil.isBlank(thirdPartyTradeNo) ? null : thirdPartyTradeNo);
        dto.setPayAmount(payAmountYuan);
        dto.setSubject(StrUtil.isBlank(subject) ? null : subject);
        dto.setMemberId(StrUtil.isBlank(memberId) ? null : memberId);
        dto.setPayTime(paySuccessTime != null ? paySuccessTime : LocalDateTime.now());
        dto.setCallbackTime(LocalDateTime.now());
        dto.setCallbackBody(rawCallbackBody);
        dto.setPayChannel("WECHAT");
        dto.setPayType(PayRecordPayTypeEnum.WECHAT_JSAPI.getCode());
        dto.setPayStatus(PayRecordStatusEnum.SUCCESS.getValue());
        dto.setBizKind(PayRecordBizKindEnum.PAY.getValue());
        dto.setCurrency(PayRecordCurrencyEnum.CNY.getCode());
        save(dto);
    }

    /**
     * 解析微信 V3 解密报文中的成功支付时间（{@code success_time}），解析失败返回 {@code null}。
     *
     * @deprecated 请使用 {@link WechatPayNotifyParseSupport#parseSuccessTime(Object)}
     */
    @Deprecated
    public static LocalDateTime parseWechatSuccessTime(Object successTimeRaw) {
        return WechatPayNotifyParseSupport.parseSuccessTime(successTimeRaw);
    }

    /**
     * 解析微信 V3 解密报文中的 {@code amount.total}（分）为金额（元）。
     *
     * @deprecated 请使用 {@link WechatPayNotifyParseSupport#parseTotalAmountYuan(Map)}
     */
    @Deprecated
    public static BigDecimal parseWechatTotalAmountYuan(Map<String, Object> decrypted) {
        return WechatPayNotifyParseSupport.parseTotalAmountYuan(decrypted);
    }

    @Transactional(readOnly = false)
    public void delete(String ids) {
        List<String> idList = Arrays.asList(ids.split(","));
        payRecordService.batchDelete(idList);
    }

    private void fillInfoNames(PayRecordInfoVO vo) {
        if (vo == null) {
            return;
        }
        vo.setBizKindName(PayRecordBizKindEnum.getDescByValue(vo.getBizKind()));
        vo.setPayStatusName(PayRecordStatusEnum.getDescByValue(vo.getPayStatus()));
        vo.setPayTypeName(PayRecordPayTypeEnum.getDescByCode(vo.getPayType()));
        vo.setBizSceneName(PayRecordBizSceneEnum.getDescByCode(vo.getBizScene()));
    }

    private void fillListNames(PayRecordListVO vo) {
        if (vo == null) {
            return;
        }
        vo.setBizKindName(PayRecordBizKindEnum.getDescByValue(vo.getBizKind()));
        vo.setPayStatusName(PayRecordStatusEnum.getDescByValue(vo.getPayStatus()));
        vo.setPayTypeName(PayRecordPayTypeEnum.getDescByCode(vo.getPayType()));
        vo.setBizSceneName(PayRecordBizSceneEnum.getDescByCode(vo.getBizScene()));
    }

    /**
     * 会员关键词 → memberIdIn 筛选条件（编排，不含会员数据组装）。
     */
    private PayRecordQueryVO prepareQuery(PayRecordQueryVO query) {
        PayRecordQueryVO q = query != null ? query : new PayRecordQueryVO();
        if (StrUtil.isBlank(q.getMemberKeyword())) {
            return q;
        }
        List<String> memberIds = memberService.findIdsByKeyword(q.getMemberKeyword().trim());
        q.setMemberKeyword(null);
        if (CollUtil.isEmpty(memberIds)) {
            return null;
        }
        q.setMemberIdIn(memberIds);
        return q;
    }

    private PageRespVO<PayRecordListVO> emptyPage(Page<PayRecord> page) {
        PageRespVO<PayRecordListVO> empty = new PageRespVO<>();
        empty.setRecords(Collections.emptyList());
        empty.setTotal(0L);
        empty.setPageNo(page.getPageNo());
        empty.setPageSize(page.getPageSize());
        empty.setPages(0L);
        return empty;
    }
}
