package com.microservices.zone.member.service.points;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.microservices.common.core.utils.DateUtils;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.core.utils.uuid.IdUtils;
import com.microservices.zone.detail.service.IZoneDetailService;
import com.microservices.zone.member.domain.ZoneMember;
import com.microservices.zone.member.domain.points.ZonePointsAccount;
import com.microservices.zone.member.domain.vo.ZonePointsTransactionInputVo;
import com.microservices.zone.member.mapper.ZoneMemberMapper;
import com.microservices.zone.member.mapper.ZonePointsAccountMapper;
import com.microservices.zone.member.service.IZonePointsTransactionService;
import com.microservices.zone.utils.ZoneConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 * 积分账户共享操作组件 —— 提供 getOrCreateAccount、recordPointsTransaction、createPointsAccountTask
 * 供各策略处理器及原有 Service 复用，避免重复代码。
 * <p>
 * 合并后只写 zone_points_transaction 单表，不再写 zone_points_event。
 *
 * @author otto
 */
@Component
public class PointsAccountOperations {
    private static final Logger logger = LoggerFactory.getLogger(PointsAccountOperations.class);

    /** 无操作人标识（如系统自动创建账户时没有具体操作人） */
    private static final Long NO_OPERATOR = 0L;

    @Autowired
    private ZoneMemberMapper zoneMemberMapper;

    @Autowired
    private ZonePointsAccountMapper zonePointsAccountMapper;

    @Autowired
    private IZoneDetailService zoneDetailService;

    @Autowired
    private IZonePointsTransactionService zonePointsTransactionService;

    /**
     * 获取或创建积分账户（仅对已审核通过的专区会员有效）
     */
    public ZonePointsAccount getOrCreateAccount(Long userId, Long zoneId) {
        ZoneMember member = zoneMemberMapper.selectZoneMember(zoneId, userId, null);
        if (member == null || !ZoneConstants.MEMBER_AUDIT_PASS.equals(member.getIsAudit())) {
            logger.warn("积分-账户操作：当前用户不是专区会员 userId={} zoneId={}", userId, zoneId);
            return null;
        }
        ZonePointsAccount account = zonePointsAccountMapper.selectZonePointsAccountByUserIdAndZoneId(userId, zoneId);
        if (account != null) {
            return account;
        }
        return doCreatePointsAccount(userId, zoneId, NO_OPERATOR);
    }

    /**
     * 创建积分账户并记录初始流水（新会员审核通过时调用）
     * <p>
     * 具备幂等保护：若账户已存在则直接返回已有账户，避免重复创建和重复发放积分。
     */
    public ZonePointsAccount createPointsAccountTask(Long userId, Long zoneId, Long operatorId) {
        // 幂等保护：检查是否已存在相同用户和专区的积分账户
        ZonePointsAccount existAccount = zonePointsAccountMapper.selectZonePointsAccountByUserIdAndZoneId(userId, zoneId);
        if (existAccount != null) {
            logger.info("积分-创建账号：积分账户已存在，跳过创建 userId={} zoneId={} accountId={}", userId, zoneId, existAccount.getId());
            return existAccount;
        }
        return doCreatePointsAccount(userId, zoneId, operatorId);
    }

    /**
     * 创建积分账户并记录初始流水（无幂等检查，调用方需确保账户不存在）
     */
    private ZonePointsAccount doCreatePointsAccount(Long userId, Long zoneId, Long operatorId) {
        ZonePointsAccount zonePointsAccount = new ZonePointsAccount();
        zonePointsAccount.setUserId(userId);
        zonePointsAccount.setZoneId(zoneId);
        zonePointsAccount.setTotalPoints(ZoneConstants.MemberAudit.POINTS);
        zonePointsAccount.setTotalEarnedPoints(ZoneConstants.MemberAudit.POINTS);
        zonePointsAccount.setTotalConsumedPoints(0);
        zonePointsAccount.setAvailablePoints(ZoneConstants.MemberAudit.POINTS);
        zonePointsAccount.setFrozenPoints(0);
        zonePointsAccount.setDeptId(zoneDetailService.selectZoneDetailById(zoneId).getDeptId());
        zonePointsAccount.setCreateTime(DateUtils.getNowDate());
        zonePointsAccount.setCreateBy("system");
        zonePointsAccountMapper.insertZonePointsAccount(zonePointsAccount);

        Long finalOperatorId = operatorId != null ? operatorId : NO_OPERATOR;
        recordPointsTransaction(zonePointsAccount.getId(),
                ZoneConstants.TransactionType.ADD,
                ZoneConstants.MemberAudit.POINTS,
                ZoneConstants.MemberAudit.POINTS, 0,
                String.format(ZoneConstants.MemberAudit.REASON_TEMPLATE, ZoneConstants.MemberAudit.POINTS),
                finalOperatorId, null, ZoneConstants.MemberAudit.SOURCE_TYPE, null,
                ZoneConstants.MemberAudit.SUBJECT, ZoneConstants.MemberAudit.SUBJECT_DESC, null);

        return zonePointsAccount;
    }

    /**
     * 记录积分变动（写入 zone_points_transaction 单表，合并了原 event + transaction 两张表），内部自动生成 eventId
     */
    public void recordPointsTransaction(Long accountId,
                                        String transactionType, int points,
                                        int availableAfter, int frozenAfter,
                                        String reason, Long operatorId, Long sourceId,
                                        String sourceType, String sourceData,
                                        String subject, String subjectDesc,
                                        String replyContent) {
        recordPointsTransaction(accountId, transactionType, points,
                availableAfter, frozenAfter, reason, operatorId, sourceId,
                sourceType, sourceData, subject, subjectDesc, replyContent, IdUtils.simpleUUID());
    }

    /**
     * 记录积分变动（写入 zone_points_transaction 单表），使用指定的 eventId
     * <p>
     * 适用于需要在外部持有 eventId 的场景（如冻结操作需要将 eventId 存入申请记录）
     */
    public void recordPointsTransaction(Long accountId,
                                        String transactionType, int points,
                                        int availableAfter, int frozenAfter,
                                        String reason, Long operatorId, Long sourceId,
                                        String sourceType, String sourceData,
                                        String subject, String subjectDesc,
                                        String replyContent,
                                        String eventId) {
        ZonePointsTransactionInputVo inputVo = new ZonePointsTransactionInputVo();
        inputVo.setEventId(eventId);
        inputVo.setEventType(sourceType);
        inputVo.setAccountId(accountId);
        inputVo.setTransactionType(transactionType);
        inputVo.setPoints(points);
        inputVo.setAvailableAfter(availableAfter);
        inputVo.setFrozenAfter(frozenAfter);
        inputVo.setOperatorId(operatorId);
        inputVo.setReason(reason);
        inputVo.setReplyContent(replyContent);
        inputVo.setSourceId(sourceId);
        inputVo.setSourceData(sourceData);
        inputVo.setSubject(subject);
        inputVo.setSubjectDesc(subjectDesc);
        zonePointsTransactionService.insertZonePointsTransaction(inputVo);
    }

    /**
     * 仅记录积分流水（不设置事件业务字段，适用于已有事件的补充流水，如驳回/通过申请时只需补充流水）
     *
     * @param eventId 关联的已有事件ID
     */
    public void recordPointsTransactionOnly(Long accountId,
                                            String transactionType, int points,
                                            int availableAfter, int frozenAfter,
                                            Long operatorId,
                                            String eventId) {
        ZonePointsTransactionInputVo inputVo = new ZonePointsTransactionInputVo();
        inputVo.setEventId(eventId);
        inputVo.setAccountId(accountId);
        inputVo.setTransactionType(transactionType);
        inputVo.setPoints(points);
        inputVo.setAvailableAfter(availableAfter);
        inputVo.setFrozenAfter(frozenAfter);
        inputVo.setOperatorId(operatorId);
        zonePointsTransactionService.insertZonePointsTransaction(inputVo);
    }

    /**
     * 补齐上下文中的 zoneId（供来自 CMS 模块的任务使用，CMS 侧任务不携带 zoneId）
     * <p>
     * 原为各 Handler 中的重复代码，抽取至此公共组件统一维护。
     */
    public void resolveZoneId(PointsTaskContext ctx) {
        if (ctx.getZoneId() != null) {
            return;
        }
        if (ctx.getDeptId() == null) {
            logger.warn("积分-{}：zoneId 和 deptId 均为空，无法确定专区 userId={} sourceId={}",
                    ctx.getSourceType(), ctx.getUserId(), ctx.getSourceId());
            return;
        }
        String zoneJson = zoneDetailService.getZoneJSONByDeptId(ctx.getDeptId());
        if (StringUtils.isEmpty(zoneJson)) {
            logger.warn("积分-{}：通过 deptId={} 未找到关联专区", ctx.getSourceType(), ctx.getDeptId());
            return;
        }
        try {
            JSONObject zoneObj = JSON.parseObject(zoneJson);
            Long zoneId = zoneObj.getLong("id");
            if (zoneId == null) {
                logger.warn("积分-{}：解析专区 JSON 未获取到 id deptId={}", ctx.getSourceType(), ctx.getDeptId());
                return;
            }
            ctx.setZoneId(zoneId);
        } catch (Exception e) {
            logger.error("积分-{}：解析专区 JSON 失败 deptId={} zoneJson={}",
                    ctx.getSourceType(), ctx.getDeptId(), zoneJson, e);
        }
    }
}