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

import com.microservices.common.core.constant.CacheConstants;
import com.microservices.common.core.constant.SecurityConstants;
import com.microservices.common.redis.service.RedisService;
import com.microservices.system.api.RemoteUserService;
import com.microservices.system.api.domain.SysUser;
import com.microservices.system.api.utils.FeignUtils;
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.detail.domain.vo.ZoneFrontRoleVo;
import com.microservices.zone.member.mapper.ZoneMemberMapper;
import com.microservices.zone.member.mapper.ZonePointsAccountMapper;
import com.microservices.zone.member.service.points.IPointsTaskHandler;
import com.microservices.zone.member.service.points.PointsAccountOperations;
import com.microservices.zone.member.service.points.PointsTaskContext;
import com.microservices.zone.resource.domain.ZoneResource;
import com.microservices.zone.resource.mapper.ZoneResourceMapper;
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;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Objects;

/**
 * 资源下载 — 作者获得积分奖励
 * <p>
 * 触发条件（全部满足才发放）：
 * <ol>
 *   <li>文件关联的资源存在且审核通过</li>
 *   <li>该资源累计获得积分未达上限（应用层检查 + DB 层原子约束双重保护）</li>
 *   <li>所属专区为积分专区</li>
 *   <li>资源作者是专区审核通过的普通会员（Member），管理员不发放积分</li>
 * </ol>
 *
 * @author otto
 */
@Component
public class DownloadResourcePointsHandler implements IPointsTaskHandler {
    private static final Logger logger = LoggerFactory.getLogger(DownloadResourcePointsHandler.class);

    @Autowired
    private PointsAccountOperations pointsAccountOperations;

    @Autowired
    private ZonePointsAccountMapper zonePointsAccountMapper;

    @Autowired
    private ZoneResourceMapper zoneResourceMapper;

    @Autowired
    private ZoneMemberMapper zoneMemberMapper;

    @Autowired
    private IZoneDetailService zoneDetailService;

    @Autowired
    private RedisService redisService;

    @Autowired
    private RemoteUserService remoteUserService;

    @Override
    public String getSourceType() {
        return ZoneConstants.DownloadResource.SOURCE_TYPE;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void handle(PointsTaskContext ctx) {
        List<ZoneResource> resourceList = zoneResourceMapper.selectZoneResourceListByFileId(ctx.getSourceId());
        if (resourceList == null || resourceList.isEmpty()) {
            logger.info("积分-资源下载：未找到关联资源或资源未通过审核 sourceId={}", ctx.getSourceId());
            return;
        }

        for (ZoneResource resource : resourceList) {
            // 积分专区检查
            Long deptId = resource.getDeptId();
            Long cachedPointsDeptId = redisService.getCacheObject(CacheConstants.ZONE_POINTS_DEPT_ID_KEY);
            if (!Objects.equals(deptId, cachedPointsDeptId)) {
                logger.info("积分-资源下载：非积分专区 deptId={}", deptId);
                continue;
            }

            String createBy = resource.getCreateBy();
            if (createBy == null) {
                logger.warn("积分-资源下载：资源作者为空 resourceId={}", resource.getId());
                continue;
            }

            // 应用层积分上限检查（快速失败——DB 层有原子约束做双重保护）
            int currentEarnedPoints = resource.getEarnedPoints() != null ? resource.getEarnedPoints() : 0;
            if (currentEarnedPoints >= ZoneConstants.DownloadResource.POINTS_LIMIT) {
                logger.info("积分-资源下载：已达积分上限 resourceId={} earnedPoints={}",
                        resource.getId(), currentEarnedPoints);
                continue;
            }

            // 通过 createBy 获取 userId（带 Redis 缓存，减少 Feign 调用）
            Long authorUserId = getUserIdByUserName(createBy);
            if (authorUserId == null) {
                logger.warn("积分-资源下载：未找到资源作者用户 createBy={}", createBy);
                continue;
            }

            // 角色检查：必须是普通会员（Member），管理员（Manager）不发放积分
            ZoneFrontRoleVo roleVo = zoneDetailService.checkUserRoleInZone(resource.getZoneId(), authorUserId);
            if (roleVo == null || !ZoneConstants.FRONT_ROLE_MEMBER.equals(roleVo.getRole())) {
                logger.info("积分-资源下载：作者不是普通会员，不发放积分 userId={} zoneId={} role={}",
                        authorUserId, resource.getZoneId(),
                        roleVo != null ? roleVo.getRole() : null);
                continue;
            }

            addPointsForAuthor(resource.getZoneId(), authorUserId, resource.getId(), ctx.getSourceId(),
                    resource.getName());
        }
    }

    /**
     * 为资源作者增加积分（核心发放逻辑——在 {@link #handle} 的事务内执行）
     */
    private void addPointsForAuthor(Long zoneId, Long authorUserId, Long resourceId, Long sourceId,
                                    String resourceName) {
        // 获取或创建积分账户（含会员资质校验）
        ZonePointsAccount account = pointsAccountOperations.getOrCreateAccount(authorUserId, zoneId);
        if (account == null) {
            logger.warn("积分-资源下载：积分账户不存在或创建失败 authorUserId={} zoneId={}", authorUserId, zoneId);
            return;
        }

        // 锁定账户行（FOR UPDATE，防并发）
        ZonePointsAccount locked = zonePointsAccountMapper.selectZonePointsAccountByIdForUpdate(account.getId());
        if (locked == null) {
            logger.warn("积分-资源下载：锁定积分账户失败 accountId={}", account.getId());
            return;
        }

        // 账户积分 +1（原子操作）
        zonePointsAccountMapper.addPointsAtomic(account.getId(), ZoneConstants.DownloadResource.POINTS);

        // 资源 earnedPoints +1（原子操作）
        zoneResourceMapper.addEarnedPointsAtomic(resourceId, ZoneConstants.DownloadResource.POINTS);

        // 重新读取资源获取最新 earnedPoints（即积分触发次数）
        ZoneResource updatedResource = zoneResourceMapper.selectZoneResourceById(resourceId);
        int totalEarnedPoints = updatedResource != null && updatedResource.getEarnedPoints() != null
                ? updatedResource.getEarnedPoints() : ZoneConstants.DownloadResource.POINTS;

        // 记录流水
        pointsAccountOperations.recordPointsTransaction(account.getId(),
                ZoneConstants.TransactionType.ADD,
                ZoneConstants.DownloadResource.POINTS,
                locked.getAvailablePoints() + ZoneConstants.DownloadResource.POINTS,
                locked.getFrozenPoints(),
                String.format(ZoneConstants.DownloadResource.REASON_TEMPLATE,
                        ZoneConstants.DownloadResource.POINTS, totalEarnedPoints),
                authorUserId, sourceId, ZoneConstants.DownloadResource.SOURCE_TYPE, null,
                ZoneConstants.DownloadResource.SUBJECT,
                String.format(ZoneConstants.DownloadResource.SUBJECT_DESC_TEMPLATE,
                        resourceName, String.valueOf(totalEarnedPoints)), null);
    }

    /**
     * 根据用户名获取用户ID（通过远程调用）
     */
    private Long getUserIdByUserName(String userName) {
        try {
            SysUser sysUser = FeignUtils.getReturnData(
                    remoteUserService.getSysUserByUserName(userName, SecurityConstants.INNER));
            if (sysUser != null) {
                return sysUser.getUserId();
            }
        } catch (Exception e) {
            logger.warn("积分-资源下载：获取用户ID失败 userName={} error={}", userName, e.getMessage());
        }
        return null;
    }
}
