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

import cn.hutool.core.util.StrUtil;
import com.jeesharp.business.modules.setting.convert.PrivacyPolicyConvert;
import com.jeesharp.business.modules.setting.dto.PrivacyPolicySaveDTO;
import com.jeesharp.business.modules.setting.entity.PrivacyPolicy;
import com.jeesharp.business.modules.setting.enums.PrivacyPolicyClientEnum;
import com.jeesharp.business.modules.setting.enums.PrivacyPolicyStatusEnum;
import com.jeesharp.business.modules.setting.service.PrivacyPolicyService;
import com.jeesharp.business.modules.setting.vo.PrivacyPolicyInfoVO;
import com.jeesharp.business.modules.setting.vo.PrivacyPolicyListVO;
import com.jeesharp.business.modules.setting.vo.PrivacyPolicyQueryVO;
import com.jeesharp.framework.pojo.Page;
import com.jeesharp.framework.pojo.query.PageRespVO;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;

/**
 * 隐私政策应用服务：后台维护与按编号向 C 端提供正文。
 * <p>
 * 端适配后的 HTML 通过 {@link PrivacyPolicyCacheService} 写入 Redis（key=id+client），
 * 命中后直接返回，避免重复跑正则；保存/删除/启停时按 id 失效，docCode→id 也独立缓存
 * 减少按 code 查询时的 DB 压力。PC 端为原文，不写缓存。
 *
 * @author jeesharp
 * @since 2026-05-20
 */
@Service
@AllArgsConstructor
@Transactional(readOnly = true)
public class PrivacyPolicyApplicationService {

    private final PrivacyPolicyService privacyPolicyService;
    private final PrivacyPolicyCacheService privacyPolicyCacheService;

    public PrivacyPolicyInfoVO get(String id) {
        return get(id, PrivacyPolicyClientEnum.DEFAULT_FOR_ADMIN);
    }

    /**
     * 按 ID 获取详情，并按指定端适配 content。
     *
     * @param id     主键
     * @param client 端枚举；为空时按 PC 原文返回
     */
    public PrivacyPolicyInfoVO get(String id, PrivacyPolicyClientEnum client) {
        if (StrUtil.isBlank(id)) {
            return null;
        }
        PrivacyPolicyClientEnum target = client == null ? PrivacyPolicyClientEnum.PC : client;

        // 命中缓存：仅当 client != PC 且能从缓存拿到「适配 HTML」时直接组装 VO（content 用缓存）
        String cached = privacyPolicyCacheService.getAdapted(id, target);
        if (cached != null) {
            PrivacyPolicy entity = privacyPolicyService.get(id);
            if (entity == null) {
                privacyPolicyCacheService.evictById(id);
                return null;
            }
            PrivacyPolicyInfoVO vo = PrivacyPolicyConvert.INSTANCE.toInfoVO(entity);
            fillNames(vo);
            vo.setClient(target.getCode());
            vo.setContent(cached);
            return vo;
        }

        PrivacyPolicy entity = privacyPolicyService.get(id);
        if (entity == null) {
            return null;
        }
        PrivacyPolicyInfoVO vo = PrivacyPolicyConvert.INSTANCE.toInfoVO(entity);
        fillNames(vo);
        applyClientAndCache(vo, target);
        return vo;
    }

    /**
     * 按文档编号获取启用中的政策（小程序端）。
     *
     * @param docCode 文档编号，如 PRIVACY、SERVICE
     */
    public PrivacyPolicyInfoVO getByDocCode(String docCode) {
        return getByDocCode(docCode, PrivacyPolicyClientEnum.DEFAULT_FOR_MOBILE);
    }

    /**
     * 按文档编号获取启用中的政策，并按指定端适配 content。
     * <p>
     * 优先查 docCode→id 的 Redis 映射；命中后转走 {@link #get(String, PrivacyPolicyClientEnum)}
     * 复用 id+client 适配缓存。任意一层缓存失效或不一致，都会兜底走 DB 并重建缓存。
     *
     * @param docCode 文档编号
     * @param client  端枚举；为空时按 MP 适配
     */
    public PrivacyPolicyInfoVO getByDocCode(String docCode, PrivacyPolicyClientEnum client) {
        if (StrUtil.isBlank(docCode)) {
            return null;
        }
        PrivacyPolicyClientEnum target = client == null ? PrivacyPolicyClientEnum.MP : client;
        String cachedId = privacyPolicyCacheService.getEnabledIdByDocCode(docCode);
        if (StrUtil.isNotBlank(cachedId)) {
            PrivacyPolicyInfoVO vo = get(cachedId, target);
            if (vo != null && docCode.trim().equalsIgnoreCase(vo.getDocCode())
                    && PrivacyPolicyStatusEnum.ENABLED.getValue().equals(vo.getStatus())) {
                return vo;
            }
            // 映射失效或文档已禁用，清掉重查
            privacyPolicyCacheService.evictDocCode(docCode);
        }

        PrivacyPolicy entity = privacyPolicyService.getEnabledByDocCode(docCode);
        if (entity == null) {
            return null;
        }
        privacyPolicyCacheService.putEnabledIdByDocCode(entity.getDocCode(), entity.getId());

        PrivacyPolicyInfoVO vo = PrivacyPolicyConvert.INSTANCE.toInfoVO(entity);
        fillNames(vo);
        applyClientAndCache(vo, target);
        return vo;
    }

    public PageRespVO<PrivacyPolicyListVO> findPage(Page<PrivacyPolicy> page, PrivacyPolicyQueryVO query) {
        Page<PrivacyPolicy> result = privacyPolicyService.findPage(page, query);
        PageRespVO<PrivacyPolicyListVO> pageResp = PrivacyPolicyConvert.INSTANCE.toPageRespVO(result);
        if (pageResp.getRecords() != null) {
            pageResp.getRecords().forEach(this::fillListNames);
        }
        return pageResp;
    }

    @Transactional(readOnly = false)
    public void save(PrivacyPolicySaveDTO dto) {
        PrivacyPolicy entity = PrivacyPolicyConvert.INSTANCE.toEntity(dto);
        String code = StrUtil.trimToEmpty(entity.getDocCode());
        if (StrUtil.isBlank(code)) {
            throw new IllegalArgumentException("文档编号不能为空");
        }
        entity.setDocCode(code.toUpperCase());
        if (StrUtil.isBlank(entity.getTitle())) {
            throw new IllegalArgumentException("标题不能为空");
        }
        if (entity.getStatus() == null) {
            entity.setStatus(PrivacyPolicyStatusEnum.ENABLED.getValue());
        } else if (PrivacyPolicyStatusEnum.fromValue(entity.getStatus()) == null) {
            throw new IllegalArgumentException("状态取值不合法");
        }
        String excludeId = StrUtil.isBlank(dto.getId()) ? null : dto.getId().trim();
        if (privacyPolicyService.countByDocCodeExcludeId(entity.getDocCode(), excludeId) > 0L) {
            throw new IllegalArgumentException("文档编号已存在");
        }
        String previousDocCode = null;
        if (StrUtil.isNotBlank(excludeId)) {
            PrivacyPolicy existing = privacyPolicyService.get(excludeId);
            if (existing != null) {
                previousDocCode = existing.getDocCode();
            }
        }
        if (StrUtil.isBlank(dto.getId())) {
            privacyPolicyService.save(entity);
        } else {
            privacyPolicyService.update(entity);
            privacyPolicyCacheService.evictById(dto.getId().trim());
        }
        privacyPolicyCacheService.evictDocCode(entity.getDocCode());
        if (StrUtil.isNotBlank(previousDocCode)
                && !previousDocCode.equalsIgnoreCase(entity.getDocCode())) {
            privacyPolicyCacheService.evictDocCode(previousDocCode);
        }
    }

    @Transactional(readOnly = false)
    public void delete(String ids) {
        List<String> idList = PrivacyPolicyCacheService.splitIds(ids);
        List<String> docCodes = new ArrayList<>(idList.size());
        for (String id : idList) {
            PrivacyPolicy entity = privacyPolicyService.get(id);
            if (entity != null && StrUtil.isNotBlank(entity.getDocCode())) {
                docCodes.add(entity.getDocCode());
            }
        }
        privacyPolicyService.batchDelete(StrUtil.splitTrim(ids, ','));
        privacyPolicyCacheService.evictByDocCodesAndIds(docCodes, idList);
    }

    @Transactional(readOnly = false)
    public void disable(String id) {
        privacyPolicyService.updateStatus(id, PrivacyPolicyStatusEnum.DISABLED.getValue());
        invalidateById(id);
    }

    @Transactional(readOnly = false)
    public void enable(String id) {
        privacyPolicyService.updateStatus(id, PrivacyPolicyStatusEnum.ENABLED.getValue());
        invalidateById(id);
    }

    private void invalidateById(String id) {
        if (StrUtil.isBlank(id)) {
            return;
        }
        PrivacyPolicy entity = privacyPolicyService.get(id);
        privacyPolicyCacheService.evictById(id);
        if (entity != null && StrUtil.isNotBlank(entity.getDocCode())) {
            privacyPolicyCacheService.evictDocCode(entity.getDocCode());
        }
    }

    private void fillNames(PrivacyPolicyInfoVO vo) {
        if (vo == null) {
            return;
        }
        vo.setStatusName(PrivacyPolicyStatusEnum.getDescByValue(vo.getStatus()));
    }

    /**
     * 把原文按 {@code client} 适配为对应端的 HTML，回填 {@code client} 字段，
     * 并把适配结果写回缓存（PC 端会被 {@link PrivacyPolicyCacheService#putAdapted} 直接跳过）。
     */
    private void applyClientAndCache(PrivacyPolicyInfoVO vo, PrivacyPolicyClientEnum client) {
        if (vo == null) {
            return;
        }
        PrivacyPolicyClientEnum target = client == null ? PrivacyPolicyClientEnum.PC : client;
        vo.setClient(target.getCode());
        String adapted = PrivacyPolicyContentAdapter.adapt(vo.getContent(), target);
        vo.setContent(adapted);
        privacyPolicyCacheService.putAdapted(vo.getId(), target, adapted);
    }

    private void fillListNames(PrivacyPolicyListVO vo) {
        if (vo == null) {
            return;
        }
        vo.setStatusName(PrivacyPolicyStatusEnum.getDescByValue(vo.getStatus()));
    }
}
