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

import com.jeesharp.business.modules.coupon.convert.CouponTemplateConvert;
import com.jeesharp.business.modules.coupon.dto.CouponTemplateSaveDTO;
import com.jeesharp.business.modules.coupon.dto.CouponTemplateStatusDTO;
import com.jeesharp.business.modules.coupon.entity.CouponTemplate;
import com.jeesharp.business.modules.coupon.enums.CouponDiscountTypeEnum;
import com.jeesharp.business.modules.coupon.enums.CouponGetTypeEnum;
import com.jeesharp.business.modules.coupon.enums.CouponTemplateStatusEnum;
import com.jeesharp.business.modules.coupon.enums.CouponTemplateTypeEnum;
import com.jeesharp.business.modules.coupon.enums.CouponUseScopeEnum;
import com.jeesharp.business.modules.coupon.enums.CouponValidityTypeEnum;
import com.jeesharp.business.modules.coupon.service.CouponTemplateService;
import com.jeesharp.business.modules.coupon.application.support.CouponSceneRegistryBridgeSupport;
import com.jeesharp.business.modules.coupon.application.support.CouponTemplateWxQrcodeSupport;
import com.jeesharp.business.modules.coupon.application.support.CouponWxSceneSupport;
import com.jeesharp.business.modules.coupon.dto.CouponTemplateClaimQrcodeGenerateDTO;
import com.jeesharp.business.modules.coupon.vo.CouponTemplateClaimQrcodeVO;
import com.jeesharp.business.modules.coupon.vo.CouponSceneOptionVO;
import com.jeesharp.business.modules.coupon.vo.CouponTemplateInfoVO;
import com.jeesharp.business.modules.coupon.vo.CouponTemplateListVO;
import com.jeesharp.business.modules.coupon.vo.CouponTemplateQueryVO;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.jeesharp.framework.exception.BizErrorException;
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 jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpHeaders;

import java.io.IOException;
import java.util.Arrays;
import java.util.List;

/**
 * 优惠券模板应用服务：管理端模板维护与状态变更。
 *
 * @author jeesharp
 * @since 2026-05-07
 */
@Service
@AllArgsConstructor
@Transactional(readOnly = true)
public class CouponTemplateApplicationService {

    private final CouponTemplateService couponTemplateService;
    private final CouponSceneRegistryBridgeSupport couponSceneRegistryBridgeSupport;
    private final CouponTemplateWxQrcodeSupport couponTemplateWxQrcodeSupport;

    public CouponTemplateInfoVO get(String id) {
        CouponTemplate entity = couponTemplateService.get(id);
        if (entity == null) {
            throw new BizErrorException("优惠券模板不存在");
        }
        CouponTemplateInfoVO vo = CouponTemplateConvert.INSTANCE.toInfoVO(entity);
        fillNames(vo);
        fillSceneName(vo);
        normalizeClaimQrcodeUrl(vo);
        return vo;
    }

    public PageRespVO<CouponTemplateListVO> findPage(Page<CouponTemplate> page, CouponTemplateQueryVO query) {
        Page<CouponTemplate> result = couponTemplateService.findPage(page, query);
        PageRespVO<CouponTemplateListVO> resp = CouponTemplateConvert.INSTANCE.toPageRespVO(result);
        if (resp.getRecords() != null) {
            for (CouponTemplateListVO row : resp.getRecords()) {
                fillNames(row);
                fillSceneName(row);
                normalizeClaimQrcodeUrl(row);
            }
        }
        return resp;
    }

    @Transactional(readOnly = false)
    public String create(CouponTemplateSaveDTO dto) {
        if (StrUtil.isNotBlank(dto.getId())) {
            throw new BizErrorException("新增请求不应携带主键 id");
        }
        couponSceneRegistryBridgeSupport.assertSceneEnabled(dto.getSceneCode(), null);
        CouponTemplate entity = CouponTemplateConvert.INSTANCE.toEntity(dto);
        if (StrUtil.isBlank(entity.getCouponCode())) {
            entity.setCouponCode(generateUniqueCouponCode(entity.getTenantId()));
        }
        entity.setIssuedCount(0);
        entity.setUsedCount(0);
        if (entity.getStatus() == null) {
            entity.setStatus(CouponTemplateStatusEnum.PENDING_START.getValue());
        }
        return couponTemplateService.createCouponTemplate(entity).getId();
    }

    @Transactional(readOnly = false)
    public void update(CouponTemplateSaveDTO dto) {
        if (StrUtil.isBlank(dto.getId())) {
            throw new BizErrorException("更新请求必须携带主键 id");
        }
        CouponTemplate existing = couponTemplateService.get(dto.getId());
        if (existing == null) {
            throw new BizErrorException("优惠券模板不存在");
        }
        couponSceneRegistryBridgeSupport.assertSceneEnabled(dto.getSceneCode(), null);
        CouponTemplate entity = CouponTemplateConvert.INSTANCE.toEntity(dto);
        entity.setCouponCode(existing.getCouponCode());
        entity.setIssuedCount(existing.getIssuedCount());
        entity.setUsedCount(existing.getUsedCount());
        couponTemplateService.updateCouponTemplate(entity);
    }

    /**
     * 新增模板时生成 {@code coupon_code}：前缀 {@code CPN_} + 雪花数字串，同租户下唯一。
     */
    private String generateUniqueCouponCode(String tenantId) {
        String prefix = "CPN_";
        for (int i = 0; i < 8; i++) {
            String candidate = prefix + IdUtil.getSnowflakeNextIdStr();
            if (candidate.length() > 32) {
                candidate = candidate.substring(0, 32);
            }
            if (couponTemplateService.findByCouponCode(candidate, tenantId) == null) {
                return candidate;
            }
        }
        throw new BizErrorException("无法生成唯一优惠券编码，请重试");
    }

    @Transactional(readOnly = false)
    public void delete(String ids) {
        List<String> idList = Arrays.asList(ids.split(","));
        for (String id : idList) {
            CouponTemplate t = couponTemplateService.get(id);
            if (t == null) {
                continue;
            }
            if (t.getIssuedCount() != null && t.getIssuedCount() > 0) {
                throw new BizErrorException("已发放的优惠券模板不可删除：" + t.getCouponName());
            }
        }
        couponTemplateService.batchDelete(idList);
    }

    @Transactional(readOnly = false)
    public void updateStatus(CouponTemplateStatusDTO dto) {
        CouponTemplate t = couponTemplateService.get(dto.getId());
        if (t == null) {
            throw new BizErrorException("优惠券模板不存在");
        }
        t.setStatus(dto.getStatus());
        couponTemplateService.update(t);
    }

    /**
     * 模板表单「使用场景」下拉（由 {@code cfg_biz_scene} COUPON 能力转换 legacy 编号）。
     */
    public List<CouponSceneOptionVO> listSceneOptions(String tenantId) {
        return couponSceneRegistryBridgeSupport.listEnabledOptions(tenantId);
    }

    /**
     * 生成（或返回已有）领券太阳码，供线下打印扫码领取。
     */
    @Transactional(readOnly = false)
    public CouponTemplateClaimQrcodeVO generateClaimQrcode(CouponTemplateClaimQrcodeGenerateDTO dto) {
        CouponTemplate template = requireTemplate(dto.getId());
        boolean force = Boolean.TRUE.equals(dto.getForce());
        if (!force
                && StrUtil.isNotBlank(template.getClaimQrcodeUrl())
                && StrUtil.isNotBlank(template.getClaimWxScene())) {
            return toClaimQrcodeVO(template);
        }
        if (!CouponGetTypeEnum.MANUAL.getValue().equals(template.getGetType())) {
            throw new BizErrorException("仅「手动领取」类型优惠券可生成领券码，当前为：" + CouponGetTypeEnum.getDescByValue(template.getGetType()));
        }
        String wxScene = StrUtil.blankToDefault(template.getClaimWxScene(), CouponWxSceneSupport.buildClaimScene(template.getId()));
        String qrcodeUrl = couponTemplateWxQrcodeSupport.generateAndUpload(wxScene, template.getId());
        qrcodeUrl = CouponTemplateWxQrcodeSupport.normalizeDisplayUrl(qrcodeUrl);
        couponTemplateService.updateClaimQrcode(template.getId(), wxScene, qrcodeUrl);
        template.setClaimWxScene(wxScene);
        template.setClaimQrcodeUrl(qrcodeUrl);
        return toClaimQrcodeVO(template);
    }

    /**
     * 管理端预览领券太阳码图片（鉴权代理）。
     */
    public void writeClaimQrcodeImage(String id, HttpServletResponse response) throws IOException {
        CouponTemplate template = requireTemplate(id);
        if (StrUtil.isBlank(template.getClaimQrcodeUrl())) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND, "领券码尚未生成");
            return;
        }
        response.setContentType(CouponTemplateWxQrcodeSupport.resolveImageContentType(template.getClaimQrcodeUrl()));
        response.setHeader(HttpHeaders.CACHE_CONTROL, "private, max-age=3600");
        couponTemplateWxQrcodeSupport.writeImage(template.getClaimQrcodeUrl(), response.getOutputStream());
    }

    private CouponTemplate requireTemplate(String id) {
        if (StrUtil.isBlank(id)) {
            throw new BizErrorException("模板主键不能为空");
        }
        CouponTemplate template = couponTemplateService.get(id.trim());
        if (template == null) {
            throw new BizErrorException("优惠券模板不存在");
        }
        return template;
    }

    private CouponTemplateClaimQrcodeVO toClaimQrcodeVO(CouponTemplate template) {
        CouponTemplateClaimQrcodeVO vo = new CouponTemplateClaimQrcodeVO();
        vo.setTemplateId(template.getId());
        vo.setCouponName(template.getCouponName());
        vo.setWxScene(template.getClaimWxScene());
        vo.setQrcodeUrl(CouponTemplateWxQrcodeSupport.normalizeDisplayUrl(template.getClaimQrcodeUrl()));
        return vo;
    }

    private void normalizeClaimQrcodeUrl(CouponTemplateInfoVO vo) {
        if (vo == null) {
            return;
        }
        vo.setClaimQrcodeUrl(CouponTemplateWxQrcodeSupport.normalizeDisplayUrl(vo.getClaimQrcodeUrl()));
    }

    private void normalizeClaimQrcodeUrl(CouponTemplateListVO vo) {
        if (vo == null) {
            return;
        }
        vo.setClaimQrcodeUrl(CouponTemplateWxQrcodeSupport.normalizeDisplayUrl(vo.getClaimQrcodeUrl()));
    }

    private void fillNames(CouponTemplateInfoVO vo) {
        vo.setCouponTypeName(CouponTemplateTypeEnum.getDescByValue(vo.getCouponType()));
        vo.setDiscountTypeName(CouponDiscountTypeEnum.getDescByValue(vo.getDiscountType()));
        vo.setUseScopeName(CouponUseScopeEnum.getDescByValue(vo.getUseScope()));
        vo.setGetTypeName(CouponGetTypeEnum.getDescByValue(vo.getGetType()));
        vo.setValidityTypeName(CouponValidityTypeEnum.getDescByValue(vo.getValidityType()));
        vo.setStatusName(CouponTemplateStatusEnum.getDescByValue(vo.getStatus()));
    }

    private void fillNames(CouponTemplateListVO vo) {
        vo.setCouponTypeName(CouponTemplateTypeEnum.getDescByValue(vo.getCouponType()));
        vo.setDiscountTypeName(CouponDiscountTypeEnum.getDescByValue(vo.getDiscountType()));
        vo.setUseScopeName(CouponUseScopeEnum.getDescByValue(vo.getUseScope()));
        vo.setGetTypeName(CouponGetTypeEnum.getDescByValue(vo.getGetType()));
        vo.setValidityTypeName(CouponValidityTypeEnum.getDescByValue(vo.getValidityType()));
        vo.setStatusName(CouponTemplateStatusEnum.getDescByValue(vo.getStatus()));
    }

    private void fillSceneName(CouponTemplateInfoVO vo) {
        if (StrUtil.isBlank(vo.getSceneCode())) {
            return;
        }
        CouponSceneOptionVO scene = couponSceneRegistryBridgeSupport.getOptionByCode(vo.getSceneCode(), null);
        if (scene != null) {
            vo.setSceneName(scene.getSceneName());
        }
    }

    private void fillSceneName(CouponTemplateListVO vo) {
        if (StrUtil.isBlank(vo.getSceneCode())) {
            return;
        }
        CouponSceneOptionVO scene = couponSceneRegistryBridgeSupport.getOptionByCode(vo.getSceneCode(), null);
        if (scene != null) {
            vo.setSceneName(scene.getSceneName());
        }
    }
}
