package com.yaoai.billing.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yaoai.billing.config.BillingConfig;
import com.yaoai.common.exception.BizException;
import com.yaoai.common.exception.ErrorCode;
import com.yaoai.domain.entity.SystemSetting;
import com.yaoai.domain.mapper.SystemSettingMapper;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.Map;

@Service
@RequiredArgsConstructor
public class BillingCostService {

    public static final String SETTING_KEY = "billing.costs";

    private final BillingConfig billingConfig;
    private final SystemSettingMapper systemSettingMapper;
    private final ObjectMapper objectMapper;

    @Cacheable(value = "billing-costs", key = "#operation")
    public BigDecimal getCost(String operation) {
        return getCosts().getOrDefault(operation, BigDecimal.ZERO);
    }

    @Cacheable(value = "billing-costs", key = "'global'")
    public Map<String, BigDecimal> getCosts() {
        Map<String, BigDecimal> costs = new LinkedHashMap<>(billingConfig.getCosts());
        SystemSetting setting = systemSettingMapper.findByKey(SETTING_KEY).orElse(null);
        if (setting == null || setting.getSettingValue() == null || setting.getSettingValue().isBlank()) {
            return costs;
        }
        try {
            Map<String, BigDecimal> overrides = objectMapper.readValue(
                    setting.getSettingValue(),
                    new TypeReference<>() {
                    }
            );
            overrides.forEach((operation, cost) -> {
                if (operation != null && cost != null) {
                    costs.put(operation, cost);
                }
            });
            return costs;
        } catch (Exception e) {
            throw new BizException(ErrorCode.INTERNAL_ERROR, "计费价格配置解析失败: " + e.getMessage());
        }
    }

    @Transactional
    @CacheEvict(value = "billing-costs", allEntries = true)
    public Map<String, BigDecimal> saveCosts(Map<String, BigDecimal> costs, Long adminId) {
        Map<String, BigDecimal> normalized = normalize(costs);
        try {
            String json = objectMapper.writeValueAsString(normalized);
            SystemSetting setting = systemSettingMapper.findByKey(SETTING_KEY).orElse(null);
            if (setting == null) {
                setting = new SystemSetting();
                setting.setSettingKey(SETTING_KEY);
                setting.setSettingValue(json);
                setting.setUpdatedAt(LocalDateTime.now());
                setting.setUpdatedBy(adminId);
                systemSettingMapper.insert(setting);
            } else {
                setting.setSettingValue(json);
                setting.setUpdatedAt(LocalDateTime.now());
                setting.setUpdatedBy(adminId);
                systemSettingMapper.updateById(setting);
            }
            return normalized;
        } catch (Exception e) {
            throw new BizException(ErrorCode.INTERNAL_ERROR, "计费价格配置保存失败: " + e.getMessage());
        }
    }

    private Map<String, BigDecimal> normalize(Map<String, BigDecimal> costs) {
        if (costs == null || costs.isEmpty()) {
            throw new BizException(ErrorCode.INVALID_PARAM, "计费价格不能为空");
        }
        Map<String, BigDecimal> normalized = new LinkedHashMap<>();
        costs.forEach((operation, cost) -> {
            if (operation == null || operation.isBlank()) {
                throw new BizException(ErrorCode.INVALID_PARAM, "计费操作不能为空");
            }
            if (cost == null || cost.compareTo(BigDecimal.ZERO) < 0) {
                throw new BizException(ErrorCode.INVALID_PARAM, "计费价格不能小于 0");
            }
            normalized.put(operation.trim(), cost);
        });
        return normalized;
    }
}
