package study.orderflow.service;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.HexFormat;
import java.util.Map;

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import study.orderflow.OrderProperties;
import study.orderflow.mapper.OrderMapper;
import study.orderflow.web.CreateOrderRequest;
import study.orderflow.web.OrderBusinessException;
import study.orderflow.web.OrderResponse;

/// 下单服务：校验、条件扣库存、订单、幂等记录在同一物理事务（T01）。
/// 异常分类链（N02）：DuplicateKey→409；锁等待超时→CannotAcquireLockException→503。
@Service
public class OrderService {

    private final OrderMapper mapper;
    private final OrderProperties props;
    private final Counter accepted;
    private final Counter rejected;

    public OrderService(OrderMapper mapper, OrderProperties props, MeterRegistry registry) {
        this.mapper = mapper;
        this.props = props;
        // U81 业务计数器：受理/拒绝。低基数标签（tenant 来自可信 claim、result 是枚举），
        // orderId 只进日志与 trace 属性，不进指标（U64 基数纪律）。
        this.accepted = Counter.builder("lab_orders_accepted")
                .description("合成订单受理数（U81）")
                .tag("tenant", "t1")
                .register(registry);
        this.rejected = Counter.builder("lab_orders_rejected")
                .description("合成订单拒绝数（U81）")
                .tag("tenant", "t1")
                .register(registry);
    }

    @Transactional(timeout = 5)
    public OrderResponse place(String trustedTenant, String idempotencyKey, CreateOrderRequest request) {
        if (request.quantity() > props.maxItemsPerOrder()) {
            rejected.increment();
            throw new OrderBusinessException("quantity_above_limit", 422);
        }
        String requestHash = sha256(request.sku() + "|" + request.quantity());
        // 幂等命中：同事务先查再写；未命中走唯一约束兜底（并发重复仍由 uk 裁决）
        Map<String, Object> existing = mapper.findIdempotency(idempotencyKey, trustedTenant);
        if (existing != null) {
            if (!requestHash.equals(existing.get("request_hash"))) {
                rejected.increment();
                throw new OrderBusinessException("idempotency_key_conflict", 409);
            }
            Map<String, Object> order = existing.get("order_id") == null
                    ? null
                    : mapper.findOrder(((Number) existing.get("order_id")).longValue(), trustedTenant);
            if (order == null) {
                throw new OrderBusinessException("idempotency_pending", 409);
            }
            accepted.increment(); // 幂等重放也是一次成功受理（语义见 U79 答案 12）
            return toResponse(order, true);
        }
        int deducted = mapper.deductStock(trustedTenant, request.sku(), request.quantity());
        if (deducted == 0) {
            rejected.increment();
            throw new OrderBusinessException("insufficient_stock", 409);
        }
        Map<String, Object> stock = mapper.findStock(trustedTenant, request.sku());
        BigDecimal unit = (BigDecimal) stock.get("unit_price");
        BigDecimal amount = unit.multiply(BigDecimal.valueOf(request.quantity()));
        Map<String, Object> order;
        long orderId;
        try {
            mapper.insertOrder(trustedTenant, request.sku(), request.quantity(), amount, idempotencyKey);
            order = mapper.findOrderByKey(idempotencyKey, trustedTenant);
            orderId = ((Number) order.get("id")).longValue();
            mapper.insertIdempotency(idempotencyKey, trustedTenant, requestHash);
            // 将 order_id 回写幂等记录，保持重复查询路径可用
            mapper.linkIdempotencyToOrder(idempotencyKey, trustedTenant, orderId);
            // U80 阶段4：发布意图与业务效果同事务提交；任何一步的唯一约束冲突都让
            // 整个本地事务回滚（含已扣库存），效果仍只有一次
            mapper.insertOutbox("e-" + orderId, orderId, 1,
                    "{\"orderId\":" + orderId + ",\"sku\":\"" + request.sku() + "\",\"quantity\":"
                            + request.quantity() + ",\"tenant\":\"" + trustedTenant + "\"}");
        } catch (DataIntegrityViolationException ex) {
            // 并发同键的同步败者：唯一约束（orders.uk_order_idem / order_idempotency.uk_idem /
            // outbox.uk_outbox_event）裁决，本事务已做的库存扣减随整体回滚
            rejected.increment();
            throw new OrderBusinessException("duplicate_request", 409);
        }
        accepted.increment();
        return toResponse(order, false);
    }

    /** 对象归属：tenant+id 双条件，查不到返回业务 404 语义（不区分不存在与跨租户）。 */
    public OrderResponse findOwned(String trustedTenant, long id) {
        Map<String, Object> order = mapper.findOrder(id, trustedTenant);
        if (order == null) {
            throw new OrderBusinessException("order_not_found", 404);
        }
        return toResponse(order, false);
    }

    private OrderResponse toResponse(Map<String, Object> order, boolean duplicated) {
        return new OrderResponse(((Number) order.get("id")).longValue(),
                (String) order.get("sku"), ((Number) order.get("quantity")).intValue(),
                (BigDecimal) order.get("amount"), (String) order.get("tenant"),
                (String) order.get("status"), duplicated);
    }

    private static String sha256(String text) {
        try {
            byte[] digest = MessageDigest.getInstance("SHA-256")
                    .digest(text.getBytes(StandardCharsets.UTF_8));
            return HexFormat.of().formatHex(digest);
        } catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
    }
}
