package com.zioinfo.mall.order;

import com.zioinfo.mall.admin.AuditService;
import com.zioinfo.mall.cart.MallCartItem;
import com.zioinfo.mall.cart.mapper.CartMapper;
import com.zioinfo.mall.integration.CrmClient;
import com.zioinfo.mall.order.mapper.OrderMapper;
import com.zioinfo.mall.product.MallProduct;
import com.zioinfo.mall.product.mapper.ProductMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 주문 서비스 — 체크아웃·상태 전이.
 *
 * <p>상태 전이 규칙(허용 맵)으로 비정상 전이를 차단한다.
 * 결제 완료(PAID) 시 재고 차감·판매수 증가, 주문 이력을 CRM 고객 인사이트로 전달.
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class OrderService {

    private final OrderMapper orderMapper;
    private final CartMapper cartMapper;
    private final ProductMapper productMapper;
    private final CrmClient crmClient;
    private final AuditService auditService;
    private final com.zioinfo.mall.realtime.OrderNotifier orderNotifier;
    private final com.zioinfo.mall.gateway.SmsSender smsSender;
    private final com.zioinfo.mall.loyalty.LoyaltyService loyaltyService;

    /** 허용 상태 전이 그래프. */
    private static final Map<String, Set<String>> TRANSITIONS = Map.of(
        "PENDING",   Set.of("PAID", "CANCELLED"),
        "PAID",      Set.of("PREPARING", "CANCELLED", "REFUNDED"),
        "PREPARING", Set.of("SHIPPED", "CANCELLED"),
        "SHIPPED",   Set.of("DELIVERED"),
        "DELIVERED", Set.of("CONFIRMED", "REFUNDED"),
        "CONFIRMED", Set.of("REFUNDED")
    );

    public List<MallOrder> myOrders(String owner, String status) {
        return orderMapper.findByOwner(owner, status);
    }

    public MallOrder detail(Long id, String owner, boolean admin) {
        MallOrder o = orderMapper.findById(id);
        if (o == null) {
            throw new RuntimeException("ERR-ORD-404: 주문을 찾을 수 없습니다");
        }
        if (!admin && !o.getOwner().equals(owner)) {
            throw new RuntimeException("ERR-ORD-403: 본인 주문만 조회할 수 있습니다");
        }
        o.setItems(orderMapper.findItems(id));
        return o;
    }

    /** 장바구니 기반 주문 생성(PENDING). */
    @Transactional
    public MallOrder checkout(String owner, CheckoutRequest req) {
        List<MallCartItem> cart = cartMapper.findByOwner(owner);
        if (cart.isEmpty()) {
            throw new RuntimeException("ERR-ORD-400: 장바구니가 비어 있습니다");
        }
        MallOrder order = new MallOrder();
        order.setOrderNo(generateOrderNo());
        order.setOwner(owner);
        order.setStoreId(req.storeId());
        order.setFulfillmentType(req.fulfillmentType() == null ? "DELIVERY" : req.fulfillmentType().toUpperCase());
        order.setStatus("PENDING");
        order.setReceiverName(req.receiverName());
        order.setReceiverPhone(req.receiverPhone());
        order.setAddress(req.address());
        order.setDeliveryZip(req.deliveryZip());
        order.setScheduledDate(req.scheduledDate());
        order.setSlotId(req.slotId());
        order.setSlotLabel(req.slotLabel());
        order.setCardMessage(req.cardMessage());
        order.setMemo(req.memo());
        order.setCouponId(req.couponId());

        BigDecimal total = BigDecimal.ZERO;
        List<MallOrderItem> items = new ArrayList<>();
        for (MallCartItem ci : cart) {
            BigDecimal unit = ci.getUnitPrice() == null ? BigDecimal.ZERO : ci.getUnitPrice();
            int qty = ci.getQuantity() == null ? 0 : ci.getQuantity();
            BigDecimal line = unit.multiply(BigDecimal.valueOf(qty));
            MallOrderItem it = new MallOrderItem();
            it.setProductId(ci.getProductId());
            it.setOptionId(ci.getOptionId());
            it.setSizeCode(ci.getSizeCode());
            it.setProductName(ci.getProductName());
            it.setOptionLabel(ci.getOptionLabel());
            it.setUnitPrice(unit);
            it.setQuantity(qty);
            it.setLineAmount(line);
            items.add(it);
            total = total.add(line);
        }
        BigDecimal discount = req.discountAmount() == null ? BigDecimal.ZERO : req.discountAmount();
        BigDecimal tax = req.taxAmount() == null ? BigDecimal.ZERO : req.taxAmount();
        BigDecimal surge = req.surgeAmount() == null ? BigDecimal.ZERO : req.surgeAmount();
        order.setTotalAmount(total);
        order.setDiscountAmount(discount);
        order.setTaxAmount(tax);
        order.setSurgeAmount(surge);
        order.setPayAmount(total.subtract(discount).add(tax).add(surge).max(BigDecimal.ZERO));
        orderMapper.insert(order);
        for (MallOrderItem it : items) {
            it.setOrderId(order.getId());
            orderMapper.insertItem(it);
        }
        cartMapper.clear(owner);
        auditService.log(owner, "ORDER_CREATE", order.getOrderNo(), "amount=" + order.getPayAmount());
        order.setItems(items);
        // 실시간 주문 알림 — 매장 관리자앱 Live Order Dashboard 푸시
        orderNotifier.broadcastNewOrder(order);
        return order;
    }

    /** 상태 전이 — 허용 맵 검증. */
    @Transactional
    public MallOrder transition(Long id, String owner, boolean admin, String to) {
        MallOrder o = detail(id, owner, admin);
        String from = o.getStatus();
        if (to == null || to.isBlank()) {
            throw new IllegalArgumentException("ERR-ORD-400: 대상 상태 필수");
        }
        to = to.toUpperCase();
        Set<String> allowed = TRANSITIONS.getOrDefault(from, Set.of());
        if (!allowed.contains(to)) {
            throw new RuntimeException("ERR-ORD-409: 허용되지 않은 상태 전이 " + from + " -> " + to);
        }
        // 고객은 취소/구매확정만 가능
        if (!admin && !(to.equals("CANCELLED") || to.equals("CONFIRMED"))) {
            throw new RuntimeException("ERR-ORD-403: 해당 상태 전이 권한 없음");
        }
        orderMapper.updateStatus(id, to);
        if ("PAID".equals(to)) {
            for (MallOrderItem it : o.getItems()) {
                productMapper.incSales(it.getProductId(), it.getQuantity());
            }
            feedCrm(o);
            // 등급별 적립률로 포인트 자동 적립 (실패 무시 — 주문 흐름 차단 금지)
            try {
                int earned = loyaltyService.accrueForOrder(o.getOwner(), o.getOrderNo(), o.getPayAmount());
                if (earned > 0) {
                    auditService.log(o.getOwner(), "LOYALTY_POINT_EARN", o.getOrderNo(), "points=" + earned);
                }
            } catch (Exception e) {
                log.warn("포인트 적립 실패(무시): {}", e.getMessage());
            }
        }
        auditService.log(admin ? AuditService.currentActor() : owner,
                "ORDER_STATUS", o.getOrderNo(), from + " -> " + to);
        MallOrder updated = detail(id, owner, admin);
        orderNotifier.broadcastStatus(updated);
        return updated;
    }

    /**
     * 제작완료 꽃사진 첨부 → 고객 MMS 발송(Proof of Quality, Ode à la Rose 패턴).
     * MMS는 게이트웨이 어댑터(기본 mock) 경유. 발송 실패는 무시(사진 저장은 유지).
     */
    @Transactional
    public MallOrder attachProofPhoto(Long id, String photoUrl, String actor) {
        MallOrder o = orderMapper.findById(id);
        if (o == null) throw new RuntimeException("ERR-ORD-404: 주문을 찾을 수 없습니다");
        orderMapper.setProofPhoto(id, photoUrl);
        try {
            if (o.getReceiverPhone() != null && photoUrl != null) {
                smsSender.sendMms(o.getReceiverPhone(),
                        "Your bouquet for order " + o.getOrderNo() + " is ready — here's a photo of today's arrangement!",
                        photoUrl);
            }
        } catch (Exception e) {
            log.warn("Proof MMS 발송 실패(무시): {}", e.getMessage());
        }
        auditService.log(actor, "ORDER_PROOF_PHOTO", o.getOrderNo(), "mms-sent");
        return orderMapper.findById(id);
    }

    /** 문앞사진(Proof of Delivery) 첨부. */
    @Transactional
    public MallOrder attachPodPhoto(Long id, String photoUrl, String actor) {
        MallOrder o = orderMapper.findById(id);
        if (o == null) throw new RuntimeException("ERR-ORD-404: 주문을 찾을 수 없습니다");
        orderMapper.setPodPhoto(id, photoUrl);
        auditService.log(actor, "ORDER_POD_PHOTO", o.getOrderNo(), "pod-attached");
        return orderMapper.findById(id);
    }

    /** 주문 이력 → CRM 고객 인사이트 (실패 무시). */
    private void feedCrm(MallOrder o) {
        try {
            Map<String, Object> payload = new LinkedHashMap<>();
            payload.put("customerKey", o.getOwner());
            payload.put("orderNo", o.getOrderNo());
            payload.put("amount", o.getPayAmount());
            payload.put("source", "MALL");
            crmClient.upsertCustomer(payload);
        } catch (Exception e) {
            log.warn("CRM 주문 피드 실패(무시): {}", e.getMessage());
        }
    }

    private String generateOrderNo() {
        return "ORD" + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))
                + String.format("%03d", new Random().nextInt(1000));
    }

    public record CheckoutRequest(Long storeId, String fulfillmentType,
                                  String receiverName, String receiverPhone, String address, String deliveryZip,
                                  java.time.LocalDate scheduledDate, Long slotId, String slotLabel,
                                  String cardMessage, String memo, Long couponId,
                                  BigDecimal discountAmount, BigDecimal taxAmount, BigDecimal surgeAmount) {}
}
