package com.zioinfo.mall.controller;

import com.zioinfo.mall.ai.OllamaClient;
import com.zioinfo.mall.common.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 고객 로열티 API — /api/mall/loyalty
 * 멤버십·포인트·등급·쿠폰·추천·구독·위시리스트 관리
 * 25개 엔드포인트
 */
@RestController
@RequestMapping("/api/mall/loyalty/v2")
@RequiredArgsConstructor
public class CustomerLoyaltyController {

    private final OllamaClient ollama;

    // ── 1. 멤버십 현황 ─────────────────────────────────────────
    @GetMapping("/members/{customerId}")
    public ApiResponse<?> getMembership(@PathVariable Long customerId) {
        return ApiResponse.ok(Map.of(
            "customerId", customerId,
            "memberSince", "2024-03-15",
            "tier", "GOLD",
            "tierProgress", Map.of("current", 2847, "nextTier", "PLATINUM", "pointsNeeded", 153, "pct", "95%"),
            "lifetimeSpend", 847.50,
            "totalOrders", 11,
            "activeSubscriptions", 1,
            "referrals", 3,
            "wishlistItems", 4,
            "status", "ACTIVE"
        ));
    }

    // ── 2. 멤버십 가입 ─────────────────────────────────────────
    @PostMapping("/members/{customerId}/enroll")
    public ApiResponse<?> enrollMembership(@PathVariable Long customerId,
                                            @RequestBody(required = false) Map<String, Object> body) {
        String referralCode = body != null ? (String) body.getOrDefault("referralCode", "") : "";
        int bonusPoints = referralCode.isEmpty() ? 0 : 200;
        return ApiResponse.ok(Map.of(
            "customerId", customerId,
            "tier", "BRONZE",
            "startingPoints", 100 + bonusPoints,
            "referralBonus", bonusPoints,
            "referralCode", referralCode,
            "enrolledAt", LocalDateTime.now().toString(),
            "welcomeOffer", "10% off your next order!",
            "message", "Welcome to Montvale Florist Rewards! You've earned " + (100 + bonusPoints) + " welcome points."
        ));
    }

    // ── 3. 포인트 잔액 ─────────────────────────────────────────
    @GetMapping("/points/{customerId}")
    public ApiResponse<?> getPoints(@PathVariable Long customerId) {
        return ApiResponse.ok(Map.of(
            "customerId", customerId,
            "currentPoints", 2847,
            "pendingPoints", 150,
            "lifetimeEarned", 4230,
            "lifetimeRedeemed", 1233,
            "expiringPoints", Map.of("amount", 200, "expiresBy", LocalDate.now().plusDays(30).toString()),
            "pointValue", "$0.01 per point",
            "redeemableValue", "$28.47"
        ));
    }

    // ── 4. 포인트 이력 ─────────────────────────────────────────
    @GetMapping("/points/{customerId}/history")
    public ApiResponse<?> getPointsHistory(@PathVariable Long customerId,
                                            @RequestParam(defaultValue = "20") int limit) {
        List<Map<String, Object>> history = List.of(
            Map.of("date", "2026-06-15", "type", "EARN", "points", +150, "description", "Order ORD-1234 — Red Roses", "balance", 2847),
            Map.of("date", "2026-06-10", "type", "EARN", "points", +80, "description", "Order ORD-1198 — Birthday Bouquet", "balance", 2697),
            Map.of("date", "2026-06-05", "type", "REDEEM", "points", -500, "description", "Discount applied to ORD-1178", "balance", 2617),
            Map.of("date", "2026-05-28", "type", "EARN", "points", +200, "description", "Referral bonus — Friend enrolled", "balance", 3117),
            Map.of("date", "2026-05-20", "type", "EARN", "points", +110, "description", "Order ORD-1145 — Subscription delivery", "balance", 2917),
            Map.of("date", "2026-05-10", "type", "BONUS", "points", +100, "description", "Birthday month bonus", "balance", 2807),
            Map.of("date", "2026-04-30", "type", "EARN", "points", +95, "description", "Order ORD-1102 — Mother's Day Bouquet", "balance", 2707)
        );
        return ApiResponse.ok(Map.of("customerId", customerId, "history", history.subList(0, Math.min(limit, history.size())), "totalTransactions", 47));
    }

    // ── 5. 포인트 적립 ─────────────────────────────────────────
    @PostMapping("/points/{customerId}/earn")
    public ApiResponse<?> earnPoints(@PathVariable Long customerId, @RequestBody Map<String, Object> body) {
        String orderId = (String) body.getOrDefault("orderId", "ORD-0000");
        double orderAmount = ((Number) body.getOrDefault("orderAmount", 0.0)).doubleValue();
        String source = (String) body.getOrDefault("source", "PURCHASE");
        // 1 point per $1, bonus for tier
        int basePoints = (int) orderAmount;
        int tierBonus = (int) (basePoints * 0.25); // 25% bonus for GOLD
        int totalPoints = basePoints + tierBonus;
        return ApiResponse.ok(Map.of(
            "customerId", customerId, "orderId", orderId,
            "basePoints", basePoints, "tierBonus", tierBonus, "totalEarned", totalPoints,
            "source", source, "newBalance", 2847 + totalPoints,
            "message", "You earned " + totalPoints + " points (including 25% Gold tier bonus)!"
        ));
    }

    // ── 6. 포인트 사용 ─────────────────────────────────────────
    @PostMapping("/points/{customerId}/redeem")
    public ApiResponse<?> redeemPoints(@PathVariable Long customerId, @RequestBody Map<String, Object> body) {
        int pointsToRedeem = ((Number) body.getOrDefault("points", 500)).intValue();
        String orderId = (String) body.getOrDefault("orderId", "");
        int currentBalance = 2847;
        if (pointsToRedeem > currentBalance) {
            return ApiResponse.fail("Insufficient points. Current balance: " + currentBalance);
        }
        if (pointsToRedeem < 100) {
            return ApiResponse.fail("Minimum redemption is 100 points");
        }
        double discountValue = pointsToRedeem * 0.01;
        return ApiResponse.ok(Map.of(
            "customerId", customerId, "orderId", orderId,
            "pointsRedeemed", pointsToRedeem,
            "discountApplied", String.format("$%.2f", discountValue),
            "remainingBalance", currentBalance - pointsToRedeem,
            "message", "Redeemed " + pointsToRedeem + " points for $" + String.format("%.2f", discountValue) + " discount!"
        ));
    }

    // ── 7. 등급 목록 ───────────────────────────────────────────
    @GetMapping("/tiers")
    public ApiResponse<?> getTiers() {
        List<Map<String, Object>> tiers = List.of(
            Map.of("tier", "BRONZE", "minPoints", 0, "maxPoints", 999, "color", "#CD7F32",
                "benefits", List.of("1 point per $1", "Birthday coupon 10%", "Free delivery on $75+"),
                "memberCount", 1247),
            Map.of("tier", "SILVER", "minPoints", 1000, "maxPoints", 1999, "color", "#C0C0C0",
                "benefits", List.of("1.15 points per $1", "Birthday coupon 15%", "Free delivery on $50+", "Priority customer service"),
                "memberCount", 483),
            Map.of("tier", "GOLD", "minPoints", 2000, "maxPoints", 4999, "color", "#FFD700",
                "benefits", List.of("1.25 points per $1", "Birthday coupon 20%", "Free delivery always", "Early access to new arrivals", "Monthly florist tips"),
                "memberCount", 287),
            Map.of("tier", "PLATINUM", "minPoints", 5000, "maxPoints", null, "color", "#E5E4E2",
                "benefits", List.of("1.50 points per $1", "Birthday coupon 25%", "Free delivery + priority", "Personal florist consultant", "Exclusive monthly gift", "VIP event invites"),
                "memberCount", 54)
        );
        return ApiResponse.ok(Map.of("tiers", tiers, "totalMembers", 2071));
    }

    // ── 8. 등급 혜택 조회 ──────────────────────────────────────
    @GetMapping("/tiers/{customerId}/benefits")
    public ApiResponse<?> getTierBenefits(@PathVariable Long customerId) {
        return ApiResponse.ok(Map.of(
            "customerId", customerId,
            "currentTier", "GOLD",
            "activeBenefits", List.of(
                Map.of("benefit", "1.25x Points Multiplier", "status", "ACTIVE", "appliedTo", "All purchases"),
                Map.of("benefit", "Free Delivery", "status", "ACTIVE", "appliedTo", "All orders"),
                Map.of("benefit", "Birthday Coupon 20%", "status", "PENDING", "appliedTo", "Birthday month", "expiresOn", "2026-07-31"),
                Map.of("benefit", "Early Access — New Arrivals", "status", "ACTIVE", "appliedTo", "Every Tuesday 8AM"),
                Map.of("benefit", "Monthly Florist Tips Newsletter", "status", "ACTIVE", "appliedTo", "Email")
            ),
            "nextTierBenefits", List.of(
                "1.50x Points Multiplier",
                "Personal Florist Consultant",
                "Exclusive Monthly Gift ($15 value)"
            )
        ));
    }

    // ── 9. 등급 업그레이드 ─────────────────────────────────────
    @PostMapping("/tiers/{customerId}/upgrade")
    @PreAuthorize("hasRole('ADMIN')")
    public ApiResponse<?> upgradeTier(@PathVariable Long customerId, @RequestBody Map<String, Object> body) {
        String newTier = (String) body.getOrDefault("tier", "GOLD");
        String reason = (String) body.getOrDefault("reason", "Admin override");
        return ApiResponse.ok(Map.of(
            "customerId", customerId,
            "previousTier", "SILVER",
            "newTier", newTier,
            "reason", reason,
            "effectiveAt", LocalDateTime.now().toString(),
            "notification", "Customer will receive an email about their tier upgrade with welcome benefits"
        ));
    }

    // ── 10. 사용 가능 쿠폰 ─────────────────────────────────────
    @GetMapping("/coupons/available")
    public ApiResponse<?> getAvailableCoupons(@RequestParam(required = false) Long customerId) {
        List<Map<String, Object>> coupons = List.of(
            Map.of("code", "BIRTHDAY20", "type", "PERCENT", "discount", "20%", "minOrder", 50.00,
                "expiresOn", LocalDate.now().plusDays(15).toString(), "usesLeft", 1, "category", "Birthday"),
            Map.of("code", "FLASH10", "type", "PERCENT", "discount", "10%", "minOrder", 30.00,
                "expiresOn", LocalDate.now().plusDays(2).toString(), "usesLeft", 1, "category", "Flash Sale"),
            Map.of("code", "REFER200", "type", "POINTS", "discount", "200 pts", "minOrder", 0.00,
                "expiresOn", LocalDate.now().plusDays(60).toString(), "usesLeft", 1, "category", "Referral"),
            Map.of("code", "FREESHIP", "type", "FREE_DELIVERY", "discount", "Free Delivery", "minOrder", 40.00,
                "expiresOn", LocalDate.now().plusDays(30).toString(), "usesLeft", 1, "category", "Delivery")
        );
        return ApiResponse.ok(Map.of("customerId", customerId, "coupons", coupons, "totalAvailable", coupons.size()));
    }

    // ── 11. 쿠폰 발급 (관리자) ─────────────────────────────────
    @PostMapping("/coupons/issue")
    @PreAuthorize("hasRole('ADMIN')")
    public ApiResponse<?> issueCoupon(@RequestBody Map<String, Object> body) {
        String code = (String) body.getOrDefault("code", "MANUAL" + System.currentTimeMillis() % 10000);
        String type = (String) body.getOrDefault("type", "PERCENT");
        Object discount = body.getOrDefault("discount", "10%");
        double minOrder = ((Number) body.getOrDefault("minOrder", 0.0)).doubleValue();
        int validDays = ((Number) body.getOrDefault("validDays", 30)).intValue();
        Object targetCustomer = body.getOrDefault("customerId", "ALL");
        return ApiResponse.ok(Map.of(
            "code", code.toUpperCase(), "type", type, "discount", discount,
            "minOrder", minOrder, "validUntil", LocalDate.now().plusDays(validDays).toString(),
            "targetCustomer", targetCustomer, "issuedAt", LocalDateTime.now().toString(),
            "message", "Coupon issued successfully"
        ));
    }

    // ── 12. 쿠폰 유효성 검증 ───────────────────────────────────
    @PostMapping("/coupons/{code}/validate")
    public ApiResponse<?> validateCoupon(@PathVariable String code,
                                          @RequestBody(required = false) Map<String, Object> body) {
        double orderAmount = body != null ? ((Number) body.getOrDefault("orderAmount", 0.0)).doubleValue() : 0.0;
        // Static validation logic
        Map<String, Object> knownCoupons = new HashMap<>();
        knownCoupons.put("BIRTHDAY20", Map.of("valid", true, "type", "PERCENT", "discount", 20, "minOrder", 50.0));
        knownCoupons.put("FLASH10", Map.of("valid", true, "type", "PERCENT", "discount", 10, "minOrder", 30.0));
        knownCoupons.put("FREESHIP", Map.of("valid", true, "type", "FREE_DELIVERY", "discount", 0, "minOrder", 40.0));
        knownCoupons.put("EXPIRED", Map.of("valid", false, "reason", "Coupon expired"));

        Object couponInfo = knownCoupons.get(code.toUpperCase());
        if (couponInfo == null) {
            return ApiResponse.ok(Map.of("code", code, "valid", false, "reason", "Coupon not found"));
        }
        @SuppressWarnings("unchecked")
        Map<String, Object> info = (Map<String, Object>) couponInfo;
        boolean valid = (boolean) info.get("valid");
        if (!valid) return ApiResponse.ok(Map.of("code", code, "valid", false, "reason", info.get("reason")));

        double minOrder = ((Number) info.get("minOrder")).doubleValue();
        if (orderAmount > 0 && orderAmount < minOrder) {
            return ApiResponse.ok(Map.of("code", code, "valid", false, "reason", "Minimum order $" + minOrder + " required. Current: $" + orderAmount));
        }
        return ApiResponse.ok(Map.of("code", code, "valid", true, "couponDetails", info));
    }

    // ── 13. 쿠폰 적용 ─────────────────────────────────────────
    @PostMapping("/coupons/{code}/apply")
    public ApiResponse<?> applyCoupon(@PathVariable String code, @RequestBody Map<String, Object> body) {
        String orderId = (String) body.getOrDefault("orderId", "");
        double orderAmount = ((Number) body.getOrDefault("orderAmount", 0.0)).doubleValue();
        // Simple discount calculation
        double discount = 0;
        String discountDescription = "";
        if (code.toUpperCase().equals("BIRTHDAY20") && orderAmount >= 50) {
            discount = orderAmount * 0.20;
            discountDescription = "20% birthday discount";
        } else if (code.toUpperCase().equals("FLASH10") && orderAmount >= 30) {
            discount = orderAmount * 0.10;
            discountDescription = "10% flash sale discount";
        } else if (code.toUpperCase().equals("FREESHIP")) {
            discount = 8.99;
            discountDescription = "Free delivery";
        }
        if (discount == 0) return ApiResponse.fail("Coupon cannot be applied to this order");
        return ApiResponse.ok(Map.of(
            "orderId", orderId, "code", code.toUpperCase(),
            "originalAmount", orderAmount, "discountApplied", Math.round(discount * 100.0) / 100.0,
            "discountDescription", discountDescription,
            "finalAmount", Math.round((orderAmount - discount) * 100.0) / 100.0,
            "appliedAt", LocalDateTime.now().toString()
        ));
    }

    // ── 14. 추천인 현황 ────────────────────────────────────────
    @GetMapping("/referrals/{customerId}")
    public ApiResponse<?> getReferrals(@PathVariable Long customerId) {
        List<Map<String, Object>> referred = List.of(
            Map.of("referredAt", "2026-05-15", "status", "CONVERTED", "earnedPoints", 200, "friendInitials", "J.K."),
            Map.of("referredAt", "2026-04-20", "status", "CONVERTED", "earnedPoints", 200, "friendInitials", "M.L."),
            Map.of("referredAt", "2026-03-10", "status", "PENDING", "earnedPoints", 0, "friendInitials", "S.P.")
        );
        return ApiResponse.ok(Map.of(
            "customerId", customerId,
            "totalReferrals", 3,
            "convertedReferrals", 2,
            "pendingReferrals", 1,
            "totalEarned", 400,
            "referrals", referred,
            "nextRewardAt", "5 referrals — Bonus $25 credit"
        ));
    }

    // ── 15. 추천 코드 생성 ─────────────────────────────────────
    @PostMapping("/referrals")
    public ApiResponse<?> generateReferralCode(@RequestBody Map<String, Object> body) {
        Long customerId = ((Number) body.getOrDefault("customerId", 0L)).longValue();
        String code = "FLOWER" + customerId + String.valueOf(System.currentTimeMillis() % 1000).toUpperCase();
        return ApiResponse.ok(Map.of(
            "customerId", customerId,
            "referralCode", code,
            "shareUrl", "https://shop.montvaleflowers.com/refer?code=" + code,
            "reward", Map.of(
                "referrer", "200 points when friend orders",
                "friend", "$10 off first order"
            ),
            "createdAt", LocalDateTime.now().toString(),
            "expiresAt", LocalDate.now().plusDays(90).toString()
        ));
    }

    // ── 16. 구독 상세 ─────────────────────────────────────────
    @GetMapping("/subscriptions/{customerId}")
    public ApiResponse<?> getSubscription(@PathVariable Long customerId) {
        Map<String, Object> sub = new LinkedHashMap<>();
        sub.put("customerId", customerId);
        sub.put("subscriptionId", "SUB-" + customerId + "-001");
        sub.put("plan", "BIWEEKLY");
        sub.put("status", "ACTIVE");
        sub.put("product", "Seasonal Mixed Bouquet");
        sub.put("pricePerDelivery", 60.00);
        sub.put("nextDelivery", LocalDate.now().plusDays(8).toString());
        sub.put("deliveryAddress", "123 Main St, Montvale, NJ 07645");
        sub.put("deliveryDay", "Saturday");
        sub.put("startDate", "2026-01-15");
        sub.put("totalDeliveries", 11);
        sub.put("savedAmount", 66.00);
        sub.put("specialInstructions", "Please leave at the door if no answer");
        return ApiResponse.ok(sub);
    }

    // ── 17. 구독 일시정지 ──────────────────────────────────────
    @PutMapping("/subscriptions/{customerId}/pause")
    public ApiResponse<?> pauseSubscription(@PathVariable Long customerId,
                                             @RequestBody(required = false) Map<String, Object> body) {
        int pauseWeeks = body != null ? ((Number) body.getOrDefault("weeks", 2)).intValue() : 2;
        String reason = body != null ? (String) body.getOrDefault("reason", "Going on vacation") : "Customer request";
        LocalDate resumeDate = LocalDate.now().plusWeeks(pauseWeeks);
        return ApiResponse.ok(Map.of(
            "customerId", customerId, "status", "PAUSED",
            "pauseReason", reason, "pauseWeeks", pauseWeeks,
            "resumeDate", resumeDate.toString(),
            "skippedDeliveries", pauseWeeks / 2,
            "message", "Subscription paused until " + resumeDate + ". We'll send a reminder before resumption."
        ));
    }

    // ── 18. 구독 재개 ─────────────────────────────────────────
    @PutMapping("/subscriptions/{customerId}/resume")
    public ApiResponse<?> resumeSubscription(@PathVariable Long customerId) {
        LocalDate nextDelivery = LocalDate.now().plusDays(7);
        return ApiResponse.ok(Map.of(
            "customerId", customerId, "status", "ACTIVE",
            "resumedAt", LocalDateTime.now().toString(),
            "nextDelivery", nextDelivery.toString(),
            "message", "Subscription resumed! Your next delivery is scheduled for " + nextDelivery
        ));
    }

    // ── 19. 구독 취소 ─────────────────────────────────────────
    @PutMapping("/subscriptions/{customerId}/cancel")
    public ApiResponse<?> cancelSubscription(@PathVariable Long customerId,
                                              @RequestBody(required = false) Map<String, Object> body) {
        String reason = body != null ? (String) body.getOrDefault("reason", "Not specified") : "Customer request";
        String feedback = body != null ? (String) body.getOrDefault("feedback", "") : "";
        return ApiResponse.ok(Map.of(
            "customerId", customerId, "status", "CANCELLED",
            "cancellationReason", reason, "feedback", feedback,
            "cancelledAt", LocalDateTime.now().toString(),
            "finalDelivery", LocalDate.now().plusDays(3).toString(),
            "earnedPoints", 2847,
            "offeredRetention", Map.of(
                "offer", "Come back anytime — your 2,847 points are saved",
                "winbackCoupon", "COMEBACK15",
                "couponValue", "15% off your next order"
            ),
            "message", "Subscription cancelled. We hope to see you again soon."
        ));
    }

    // ── 20. 위시리스트 ────────────────────────────────────────
    @GetMapping("/wishlist/{customerId}")
    public ApiResponse<?> getWishlist(@PathVariable Long customerId) {
        List<Map<String, Object>> items = List.of(
            Map.of("productId", 1L, "name", "Grand Luxury Rose Tower", "price", 350.00,
                "addedOn", "2026-06-10", "inStock", true, "priceDropped", false),
            Map.of("productId", 2L, "name", "Pink Peony Paradise", "price", 110.00,
                "addedOn", "2026-05-28", "inStock", true, "priceDropped", true, "previousPrice", 130.00),
            Map.of("productId", 3L, "name", "Orchid Elegance Display", "price", 280.00,
                "addedOn", "2026-05-15", "inStock", false, "priceDropped", false),
            Map.of("productId", 4L, "name", "Bohemian Wildflowers XL", "price", 95.00,
                "addedOn", "2026-04-30", "inStock", true, "priceDropped", false)
        );
        return ApiResponse.ok(Map.of(
            "customerId", customerId, "items", items, "count", items.size(),
            "priceDropAlerts", 1, "backInStockAlerts", 0
        ));
    }

    // ── 21. 위시리스트 추가 ────────────────────────────────────
    @PostMapping("/wishlist/{customerId}")
    public ApiResponse<?> addToWishlist(@PathVariable Long customerId, @RequestBody Map<String, Object> body) {
        Long productId = ((Number) body.getOrDefault("productId", 0L)).longValue();
        boolean priceAlert = (boolean) body.getOrDefault("priceAlert", true);
        boolean stockAlert = (boolean) body.getOrDefault("stockAlert", true);
        return ApiResponse.ok(Map.of(
            "customerId", customerId, "productId", productId,
            "addedAt", LocalDateTime.now().toString(),
            "priceAlertEnabled", priceAlert, "stockAlertEnabled", stockAlert,
            "wishlistCount", 5,
            "message", "Added to wishlist. We'll notify you of price drops and stock changes."
        ));
    }

    // ── 22. 위시리스트 제거 ────────────────────────────────────
    @DeleteMapping("/wishlist/{customerId}/{productId}")
    public ApiResponse<?> removeFromWishlist(@PathVariable Long customerId, @PathVariable Long productId) {
        return ApiResponse.ok(Map.of(
            "customerId", customerId, "productId", productId,
            "removedAt", LocalDateTime.now().toString(),
            "remainingWishlistCount", 3,
            "message", "Item removed from wishlist"
        ));
    }

    // ── 23. 재주문 추천 (자주 구매) ──────────────────────────
    @GetMapping("/reorder/{customerId}")
    public ApiResponse<?> getReorderSuggestions(@PathVariable Long customerId) {
        List<Map<String, Object>> suggestions = List.of(
            Map.of("productId", 1L, "name", "Classic Red Dozen Roses",
                "lastOrdered", "2026-06-01", "timesOrdered", 4, "avgInterval", "18 days",
                "daysOverdue", 7, "suggestionStrength", "STRONG",
                "price", 89.00, "discountForReorder", "5% loyalty discount"),
            Map.of("productId", 5L, "name", "Seasonal Mixed Bouquet",
                "lastOrdered", "2026-05-25", "timesOrdered", 3, "avgInterval", "21 days",
                "daysOverdue", 0, "suggestionStrength", "MEDIUM",
                "price", 70.00, "discountForReorder", null),
            Map.of("productId", 3L, "name", "Pink Peony Delight",
                "lastOrdered", "2026-05-10", "timesOrdered", 2, "avgInterval", "30 days",
                "daysOverdue", 8, "suggestionStrength", "MEDIUM",
                "price", 110.00, "discountForReorder", null)
        );
        return ApiResponse.ok(Map.of(
            "customerId", customerId,
            "reorderSuggestions", suggestions,
            "tip", "Your usual flowers are ready to reorder — 1-click to repeat your favorites!"
        ));
    }

    // ── 24. AI 고객 개인화 (Ollama) ───────────────────────────
    @PostMapping("/ai-personalize")
    public ApiResponse<?> aiPersonalize(@RequestBody Map<String, Object> body) {
        Long customerId = ((Number) body.getOrDefault("customerId", 0L)).longValue();
        String tier = (String) body.getOrDefault("tier", "GOLD");
        @SuppressWarnings("unchecked")
        List<String> purchaseHistory = (List<String>) body.getOrDefault("purchaseHistory", List.of("Red Roses", "Peonies"));
        @SuppressWarnings("unchecked")
        List<String> occasions = (List<String>) body.getOrDefault("occasions", List.of("anniversary", "birthday"));
        String prompt = "You are a loyalty AI for a flower shop. Customer tier: " + tier
            + ". Past purchases: " + purchaseHistory + ". Occasions: " + occasions
            + ". Provide: 3 personalized next-purchase recommendations with reasons, 1 exclusive loyalty offer, 1 engagement tip. Keep it warm and personal.";
        String aiResponse = ollama.generate(prompt);
        if (aiResponse.isEmpty()) {
            aiResponse = "As a valued GOLD member, we recommend: 1) Premium Rose Bundle ($99) — perfect for upcoming occasions, 2) Seasonal Subscription — save 15% monthly, 3) Exclusive Orchid Collection — new arrivals just for GOLD members. Special offer: Double points this weekend!";
        }
        return ApiResponse.ok(Map.of(
            "customerId", customerId, "tier", tier,
            "aiPersonalization", aiResponse,
            "generatedAt", LocalDateTime.now().toString()
        ));
    }

    // ── 25. 멤버십 전체 통계 ───────────────────────────────────
    @GetMapping("/stats")
    @PreAuthorize("hasRole('ADMIN')")
    public ApiResponse<?> getMembershipStats() {
        Map<String, Object> stats = new LinkedHashMap<>();
        stats.put("totalMembers", 2071);
        stats.put("activeMembers", 1843);
        stats.put("tierBreakdown", Map.of("BRONZE", 1247, "SILVER", 483, "GOLD", 287, "PLATINUM", 54));
        stats.put("totalPointsIssued", 8_420_000);
        stats.put("totalPointsRedeemed", 3_180_000);
        stats.put("pointsLiability", "$52,400");
        stats.put("avgPointsPerMember", 2847);
        stats.put("topEarnerThisMonth", Map.of("customerId", 1042L, "pointsEarned", 1250));
        stats.put("subscriptionMetrics", Map.of("activeSubscriptions", 287, "churnThisMonth", 9, "newSubscriptions", 41));
        stats.put("couponMetrics", Map.of("couponsIssued", 1240, "couponsRedeemed", 987, "redemptionRate", "79.6%", "avgDiscountValue", "$12.40"));
        stats.put("referralMetrics", Map.of("totalReferrals", 312, "convertedReferrals", 247, "conversionRate", "79.2%"));
        stats.put("reportDate", LocalDate.now().toString());
        return ApiResponse.ok(stats);
    }
}
