package com.zioinfo.mall.controller;

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

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

/**
 * 주문 분석 API — /api/mall/analytics
 * 매출·고객·배송·재고·구독 분석 + Ollama AI 수요 예측
 * 25개 엔드포인트
 */
@RestController
@RequestMapping("/api/mall/analytics/v2")
@RequiredArgsConstructor
public class OrderAnalyticsController {

    private final OllamaClient ollama;
    private final Random rng = new Random(42);

    // ── 1. 일별 매출 ───────────────────────────────────────────
    @GetMapping("/sales/daily")
    public ApiResponse<?> getSalesDaily(@RequestParam(required = false) String storeId,
                                         @RequestParam(defaultValue = "7") int days) {
        List<Map<String, Object>> daily = IntStream.range(0, days).mapToObj(i -> {
            LocalDate date = LocalDate.now().minusDays(days - 1 - i);
            double revenue = 800 + rng.nextInt(1200);
            int orders = 10 + rng.nextInt(30);
            return (Map<String, Object>) new LinkedHashMap<String, Object>() {{
                put("date", date.toString());
                put("revenue", Math.round(revenue * 100.0) / 100.0);
                put("orders", orders);
                put("avgOrderValue", Math.round((revenue / orders) * 100.0) / 100.0);
                put("storeId", storeId != null ? storeId : "all");
            }};
        }).collect(Collectors.toList());
        double totalRevenue = daily.stream().mapToDouble(m -> (double) m.get("revenue")).sum();
        return ApiResponse.ok(Map.of("period", days + " days", "daily", daily,
            "totalRevenue", Math.round(totalRevenue * 100.0) / 100.0, "currency", "USD"));
    }

    // ── 2. 주별 매출 추이 ──────────────────────────────────────
    @GetMapping("/sales/weekly")
    public ApiResponse<?> getSalesWeekly(@RequestParam(defaultValue = "8") int weeks) {
        List<Map<String, Object>> weekly = IntStream.range(0, weeks).mapToObj(i -> {
            LocalDate weekStart = LocalDate.now().minusWeeks(weeks - 1 - i).with(java.time.DayOfWeek.MONDAY);
            double revenue = 5000 + rng.nextInt(4000);
            int orders = 60 + rng.nextInt(80);
            return (Map<String, Object>) new LinkedHashMap<String, Object>() {{
                put("weekStart", weekStart.toString());
                put("weekEnd", weekStart.plusDays(6).toString());
                put("revenue", Math.round(revenue * 100.0) / 100.0);
                put("orders", orders);
                put("growthPct", (rng.nextInt(30) - 5) + "%");
            }};
        }).collect(Collectors.toList());
        return ApiResponse.ok(Map.of("weeks", weeks, "weekly", weekly));
    }

    // ── 3. 월별 매출 추이 ──────────────────────────────────────
    @GetMapping("/sales/monthly")
    public ApiResponse<?> getSalesMonthly(@RequestParam(defaultValue = "12") int months) {
        List<Map<String, Object>> monthly = IntStream.range(0, months).mapToObj(i -> {
            LocalDate m = LocalDate.now().minusMonths(months - 1 - i).withDayOfMonth(1);
            double revenue = 20000 + rng.nextInt(15000);
            String[] peakMonths = {"02", "05", "06", "12"};
            boolean isPeak = Arrays.asList(peakMonths).contains(String.format("%02d", m.getMonthValue()));
            if (isPeak) revenue *= 1.4;
            final double finalRevenue = revenue;
            return (Map<String, Object>) new LinkedHashMap<String, Object>() {{
                put("month", m.toString().substring(0, 7));
                put("revenue", Math.round(finalRevenue * 100.0) / 100.0);
                put("orders", (int)(finalRevenue / 80));
                put("isPeakSeason", isPeak);
            }};
        }).collect(Collectors.toList());
        return ApiResponse.ok(Map.of("months", months, "monthly", monthly, "peakSeasons", List.of("Feb (Valentine)", "May (Mother's Day)", "Jun (Weddings)", "Dec (Holidays)")));
    }

    // ── 4. 연간 매출 비교 ──────────────────────────────────────
    @GetMapping("/sales/yearly")
    public ApiResponse<?> getSalesYearly(@RequestParam(defaultValue = "3") int years) {
        int currentYear = LocalDate.now().getYear();
        List<Map<String, Object>> yearly = IntStream.range(0, years).mapToObj(i -> {
            int year = currentYear - (years - 1 - i);
            double base = 200000 + (i * 25000);
            double revenue = base + rng.nextInt(30000);
            Map<String, Object> row = new LinkedHashMap<>();
            row.put("year", year);
            row.put("revenue", Math.round(revenue * 100.0) / 100.0);
            row.put("orders", (int)(revenue / 75));
            row.put("yoyGrowth", i == 0 ? "baseline" : "+" + (8 + rng.nextInt(12)) + "%");
            return row;
        }).collect(Collectors.toList());
        return ApiResponse.ok(Map.of("years", years, "yearly", yearly));
    }

    // ── 5. 상위 판매 상품 ──────────────────────────────────────
    @GetMapping("/products/top")
    public ApiResponse<?> getTopProducts(@RequestParam(defaultValue = "10") int limit,
                                          @RequestParam(defaultValue = "30") int days) {
        List<Map<String, Object>> top = List.of(
            Map.of("rank", 1, "productId", 1L, "name", "Classic Red Dozen Roses", "unitsSold", 1247, "revenue", 111003.00, "rating", 4.9),
            Map.of("rank", 2, "productId", 2L, "name", "Sunflower Happiness Bouquet", "unitsSold", 983, "revenue", 58980.00, "rating", 4.8),
            Map.of("rank", 3, "productId", 3L, "name", "Pink Peony Delight", "unitsSold", 754, "revenue", 82940.00, "rating", 4.9),
            Map.of("rank", 4, "productId", 4L, "name", "White Lily Serenity", "unitsSold", 631, "revenue", 59945.00, "rating", 4.7),
            Map.of("rank", 5, "productId", 5L, "name", "Rainbow Spring Mix", "unitsSold", 598, "revenue", 41860.00, "rating", 4.8),
            Map.of("rank", 6, "productId", 6L, "name", "Garden Romance Bouquet", "unitsSold", 521, "revenue", 52100.00, "rating", 4.7),
            Map.of("rank", 7, "productId", 7L, "name", "Bohemian Wildflowers", "unitsSold", 489, "revenue", 34230.00, "rating", 4.6),
            Map.of("rank", 8, "productId", 8L, "name", "Premium White Collection", "unitsSold", 445, "revenue", 57850.00, "rating", 4.8),
            Map.of("rank", 9, "productId", 9L, "name", "Tropical Paradise Mix", "unitsSold", 412, "revenue", 39140.00, "rating", 4.7),
            Map.of("rank", 10, "productId", 10L, "name", "Lavender Dreams Bundle", "unitsSold", 387, "revenue", 21285.00, "rating", 4.6)
        );
        return ApiResponse.ok(Map.of("period", days + " days", "topProducts", top.subList(0, Math.min(limit, top.size()))));
    }

    // ── 6. 하위 판매 상품 ──────────────────────────────────────
    @GetMapping("/products/low")
    public ApiResponse<?> getLowProducts(@RequestParam(defaultValue = "5") int limit) {
        List<Map<String, Object>> low = List.of(
            Map.of("productId", 51L, "name", "Exotic Bird of Paradise", "unitsSold", 3, "daysInStock", 45, "recommendation", "Discount or bundle"),
            Map.of("productId", 52L, "name", "Neon Pink Orchid Set", "unitsSold", 5, "daysInStock", 30, "recommendation", "Feature in flash deal"),
            Map.of("productId", 53L, "name", "Autumn Dried Arrangement", "unitsSold", 7, "daysInStock", 60, "recommendation", "Seasonal repositioning"),
            Map.of("productId", 54L, "name", "Abstract Succulent Bowl", "unitsSold", 8, "daysInStock", 35, "recommendation", "Add to office decor bundle"),
            Map.of("productId", 55L, "name", "Mini Cactus Terrariums", "unitsSold", 9, "daysInStock", 28, "recommendation", "Gift shop upsell")
        );
        return ApiResponse.ok(Map.of("lowPerformers", low.subList(0, Math.min(limit, low.size())), "actionRequired", true));
    }

    // ── 7. 신규 고객 추이 ──────────────────────────────────────
    @GetMapping("/customers/new")
    public ApiResponse<?> getNewCustomers(@RequestParam(defaultValue = "30") int days) {
        List<Map<String, Object>> trend = IntStream.range(0, days).filter(i -> i % 7 == 0 || i == days - 1).mapToObj(i -> {
            LocalDate date = LocalDate.now().minusDays(days - 1 - i);
            int newCustomers = 5 + rng.nextInt(15);
            Map<String, Object> row = new LinkedHashMap<>();
            row.put("date", date.toString());
            row.put("newCustomers", newCustomers);
            row.put("acquisitionChannel", i % 3 == 0 ? "organic" : i % 3 == 1 ? "referral" : "paid");
            return row;
        }).collect(Collectors.toList());
        int totalNew = trend.stream().mapToInt(m -> (int) m.get("newCustomers")).sum();
        return ApiResponse.ok(Map.of("period", days + " days", "totalNewCustomers", totalNew, "trend", trend));
    }

    // ── 8. 재구매율 분석 ───────────────────────────────────────
    @GetMapping("/customers/retention")
    public ApiResponse<?> getRetentionRate() {
        return ApiResponse.ok(Map.of(
            "overall30DayRetention", "34%",
            "overall90DayRetention", "52%",
            "subscriptionRetention", "78%",
            "oneTimeCustomerRepurchase", "22%",
            "loyaltyMemberRetention", "68%",
            "topRetentionDrivers", List.of("Subscription program", "Loyalty points", "Birthday reminders", "Quality consistency"),
            "churnRisk", Map.of("count", 47, "reason", "No order in 45+ days", "recommendedAction", "Send re-engagement email with 15% coupon")
        ));
    }

    // ── 9. 고객 생애 가치 (LTV) ────────────────────────────────
    @GetMapping("/customers/ltv")
    public ApiResponse<?> getCustomerLtv(@RequestParam(defaultValue = "12") int months) {
        return ApiResponse.ok(Map.of(
            "period", months + " months",
            "avgLtv", 287.50,
            "ltvByTier", Map.of(
                "BRONZE", 95.00, "SILVER", 185.00, "GOLD", 340.00, "PLATINUM", 620.00
            ),
            "subscriptionLtv", 520.00,
            "oneTimeLtv", 85.00,
            "projectedAnnualLtv", 3150.00,
            "topLtvSegments", List.of("Subscribers", "Wedding clients", "Corporate accounts")
        ));
    }

    // ── 10. 배송 성과 ──────────────────────────────────────────
    @GetMapping("/delivery/performance")
    public ApiResponse<?> getDeliveryPerformance(@RequestParam(defaultValue = "30") int days) {
        return ApiResponse.ok(Map.of(
            "period", days + " days",
            "totalDeliveries", 1847,
            "onTimeRate", "94.2%",
            "lateDeliveries", 107,
            "avgDeliveryMinutes", 48,
            "slaCompliance", "96.8%",
            "byTimeSlot", List.of(
                Map.of("slot", "9AM-12PM", "onTime", "97%", "count", 412),
                Map.of("slot", "12PM-3PM", "onTime", "95%", "count", 638),
                Map.of("slot", "3PM-6PM", "onTime", "91%", "count", 797)
            ),
            "lateReasons", Map.of("traffic", "42%", "prepTime", "28%", "weatherDelay", "18%", "other", "12%")
        ));
    }

    // ── 11. 배송 구역별 주문 분포 ──────────────────────────────
    @GetMapping("/delivery/zones")
    public ApiResponse<?> getDeliveryZones() {
        List<Map<String, Object>> zones = List.of(
            Map.of("zip", "07645", "city", "Montvale", "orders", 342, "pct", "18.5%", "avgDeliveryTime", "32 min"),
            Map.of("zip", "07452", "city", "Glen Rock", "orders", 287, "pct", "15.5%", "avgDeliveryTime", "38 min"),
            Map.of("zip", "07401", "city", "Allendale", "orders", 254, "pct", "13.7%", "avgDeliveryTime", "35 min"),
            Map.of("zip", "07410", "city", "Fair Lawn", "orders", 231, "pct", "12.5%", "avgDeliveryTime", "44 min"),
            Map.of("zip", "07458", "city", "Park Ridge", "orders", 198, "pct", "10.7%", "avgDeliveryTime", "29 min"),
            Map.of("zip", "07663", "city", "Saddle Brook", "orders", 176, "pct", "9.5%", "avgDeliveryTime", "41 min"),
            Map.of("zip", "07675", "city", "Woodcliff Lake", "orders", 163, "pct", "8.8%", "avgDeliveryTime", "27 min"),
            Map.of("zip", "07078", "city", "Short Hills", "orders", 101, "pct", "5.5%", "avgDeliveryTime", "55 min")
        );
        return ApiResponse.ok(Map.of("totalZones", zones.size(), "zones", zones, "highDemandZip", "07645"));
    }

    // ── 12. 피크 주문 시간대 ───────────────────────────────────
    @GetMapping("/peak-hours")
    public ApiResponse<?> getPeakHours() {
        List<Map<String, Object>> hours = IntStream.range(7, 21).mapToObj(h -> {
            int orders = switch(h) {
                case 8, 9 -> 45 + rng.nextInt(20);
                case 11, 12 -> 85 + rng.nextInt(30);
                case 14, 15 -> 65 + rng.nextInt(25);
                case 17, 18 -> 90 + rng.nextInt(35);
                default -> 25 + rng.nextInt(20);
            };
            Map<String, Object> row = new LinkedHashMap<>();
            row.put("hour", String.format("%02d:00", h));
            row.put("orders", orders);
            row.put("isPeak", orders > 80);
            return row;
        }).collect(Collectors.toList());
        return ApiResponse.ok(Map.of("peakHours", List.of("11:00-13:00", "17:00-19:00"), "hourlyBreakdown", hours,
            "busiest", "6:00 PM", "quietest", "8:00 AM"));
    }

    // ── 13. 피크 요일 분석 ─────────────────────────────────────
    @GetMapping("/peak-days")
    public ApiResponse<?> getPeakDays() {
        List<Map<String, Object>> days = List.of(
            Map.of("day", "Monday", "avgOrders", 38, "peakTime", "6PM-7PM", "topProduct", "Office Desk Arrangement"),
            Map.of("day", "Tuesday", "avgOrders", 42, "peakTime", "12PM-1PM", "topProduct", "Lunch Delivery Bouquet"),
            Map.of("day", "Wednesday", "avgOrders", 45, "peakTime", "5PM-6PM", "topProduct", "Midweek Pick-Me-Up"),
            Map.of("day", "Thursday", "avgOrders", 41, "peakTime", "4PM-5PM", "topProduct", "Weekend Prep Flowers"),
            Map.of("day", "Friday", "avgOrders", 78, "peakTime", "3PM-5PM", "topProduct", "Weekend Date Night Roses"),
            Map.of("day", "Saturday", "avgOrders", 112, "peakTime", "10AM-12PM", "topProduct", "Wedding Day Florals"),
            Map.of("day", "Sunday", "avgOrders", 65, "peakTime", "11AM-1PM", "topProduct", "Sunday Brunch Bouquet")
        );
        return ApiResponse.ok(Map.of("peakDay", "Saturday", "quietDay", "Monday", "weeklyBreakdown", days));
    }

    // ── 14. 매장별 매출 비교 ───────────────────────────────────
    @GetMapping("/stores/comparison")
    public ApiResponse<?> getStoresComparison(@RequestParam(defaultValue = "30") int days) {
        List<Map<String, Object>> stores = List.of(
            Map.of("storeId", 1L, "storeName", "Montvale Main", "revenue", 42500.00, "orders", 512, "avgTicket", 83.00, "rank", 1),
            Map.of("storeId", 2L, "storeName", "Glen Rock Branch", "revenue", 35200.00, "orders", 441, "avgTicket", 79.80, "rank", 2),
            Map.of("storeId", 3L, "storeName", "Fair Lawn Express", "revenue", 28700.00, "orders", 387, "avgTicket", 74.10, "rank", 3),
            Map.of("storeId", 4L, "storeName", "Allendale Boutique", "revenue", 24900.00, "orders", 301, "avgTicket", 82.70, "rank", 4),
            Map.of("storeId", 5L, "storeName", "Park Ridge Studio", "revenue", 21300.00, "orders", 265, "avgTicket", 80.40, "rank", 5)
        );
        return ApiResponse.ok(Map.of("period", days + " days", "stores", stores, "networkTotal", 152600.00));
    }

    // ── 15. 매장 성과 랭킹 ─────────────────────────────────────
    @GetMapping("/stores/ranking")
    public ApiResponse<?> getStoresRanking(@RequestParam(defaultValue = "revenue") String metric) {
        List<Map<String, Object>> ranking = List.of(
            Map.of("rank", 1, "store", "Montvale Main", "score", 98, "badge", "TOP PERFORMER", "metric", metric),
            Map.of("rank", 2, "store", "Glen Rock Branch", "score", 91, "badge", "EXCELLENT", "metric", metric),
            Map.of("rank", 3, "store", "Fair Lawn Express", "score", 84, "badge", "GOOD", "metric", metric),
            Map.of("rank", 4, "store", "Allendale Boutique", "score", 79, "badge", "GOOD", "metric", metric),
            Map.of("rank", 5, "store", "Park Ridge Studio", "score", 73, "badge", "IMPROVING", "metric", metric)
        );
        return ApiResponse.ok(Map.of("metric", metric, "ranking", ranking, "lastUpdated", LocalDateTime.now().toString()));
    }

    // ── 16. 재고 회전율 ────────────────────────────────────────
    @GetMapping("/inventory/turnover")
    public ApiResponse<?> getInventoryTurnover(@RequestParam(defaultValue = "30") int days) {
        List<Map<String, Object>> turnover = List.of(
            Map.of("product", "Red Roses", "daysToSell", 1.2, "turnoverRate", 25.0, "status", "FAST"),
            Map.of("product", "Sunflowers", "daysToSell", 1.8, "turnoverRate", 16.7, "status", "FAST"),
            Map.of("product", "Peonies", "daysToSell", 2.1, "turnoverRate", 14.3, "status", "GOOD"),
            Map.of("product", "Orchids", "daysToSell", 4.5, "turnoverRate", 6.7, "status", "SLOW"),
            Map.of("product", "Dried Arrangements", "daysToSell", 12.0, "turnoverRate", 2.5, "status", "VERY SLOW")
        );
        return ApiResponse.ok(Map.of("period", days + " days", "avgTurnoverDays", 3.2, "fastMovers", 3, "slowMovers", 2, "products", turnover));
    }

    // ── 17. 재고 수요 예측 (Ollama) ────────────────────────────
    @GetMapping("/inventory/forecast")
    public ApiResponse<?> getForecastInventory(@RequestParam(defaultValue = "7") int forecastDays) {
        String prompt = "Flower shop inventory AI. Forecast demand for next " + forecastDays
            + " days. Current date: " + LocalDate.now()
            + ". Upcoming holidays: Valentine's Day (Feb 14), Mother's Day (May 2nd Sun). "
            + "Suggest: red roses units, mixed bouquets, white lilies, sunflowers. "
            + "Give numbers and reasoning concisely.";
        String forecast = ollama.generate(prompt);
        if (forecast.isEmpty()) {
            forecast = "Forecast: Red Roses 200 units (+30% for weekend), Mixed Bouquets 150 units, White Lilies 80 units, Sunflowers 120 units.";
        }
        return ApiResponse.ok(Map.of(
            "forecastDays", forecastDays,
            "aiForecast", forecast,
            "staticForecast", Map.of(
                "redRoses", 200, "mixedBouquets", 150, "whiteLilies", 80, "sunflowers", 120
            ),
            "confidence", "87%",
            "generatedAt", LocalDateTime.now().toString()
        ));
    }

    // ── 18. 프로모션 효과 분석 ─────────────────────────────────
    @GetMapping("/promotions/effectiveness")
    public ApiResponse<?> getPromotionsEffectiveness() {
        List<Map<String, Object>> promos = List.of(
            Map.of("promo", "Valentine's Day 20% Off", "revenue", 18500.00, "orders", 230, "roi", "340%", "period", "Feb 10-14"),
            Map.of("promo", "Mother's Day Bundle Deal", "revenue", 22000.00, "orders", 275, "roi", "420%", "period", "May 1-12"),
            Map.of("promo", "Flash Sale Tuesday", "revenue", 4200.00, "orders", 68, "roi", "180%", "period", "weekly"),
            Map.of("promo", "Loyalty Double Points", "revenue", 8700.00, "orders", 112, "roi", "210%", "period", "monthly"),
            Map.of("promo", "First Order 15% Off", "revenue", 3200.00, "orders", 51, "roi", "165%", "period", "ongoing")
        );
        return ApiResponse.ok(Map.of("promotions", promos, "bestPerformer", "Mother's Day Bundle Deal", "totalPromoRevenue", 56600.00));
    }

    // ── 19. 리뷰 감성 분석 (Ollama) ───────────────────────────
    @GetMapping("/reviews/sentiment")
    public ApiResponse<?> getReviewSentiment(@RequestParam(defaultValue = "30") int days) {
        String prompt = "Analyze flower shop review sentiment. Sample reviews: "
            + "'Amazing fresh flowers, arrived on time!', 'Roses wilted next day - disappointed', "
            + "'Perfect birthday surprise, will order again', 'Delivery was late but flowers were beautiful'. "
            + "Give: positive%, negative%, neutral%, top 3 praise points, top 3 complaint areas. Concise format.";
        String analysis = ollama.generate(prompt);
        if (analysis.isEmpty()) {
            analysis = "Positive: 78%, Negative: 12%, Neutral: 10%. Top praise: freshness, packaging, delivery speed. Issues: occasional wilting, rare late deliveries.";
        }
        return ApiResponse.ok(Map.of(
            "period", days + " days",
            "totalReviews", 347,
            "avgRating", 4.6,
            "sentiment", Map.of("positive", "78%", "neutral", "10%", "negative", "12%"),
            "aiAnalysis", analysis,
            "topPraise", List.of("Freshness", "Beautiful arrangement", "On-time delivery"),
            "topComplaints", List.of("Occasional wilting", "Late delivery", "Missing card")
        ));
    }

    // ── 20. 환불 분석 ──────────────────────────────────────────
    @GetMapping("/refunds/analysis")
    public ApiResponse<?> getRefundsAnalysis(@RequestParam(defaultValue = "30") int days) {
        return ApiResponse.ok(Map.of(
            "period", days + " days",
            "totalRefunds", 23,
            "refundRate", "1.24%",
            "totalRefundAmount", 1847.50,
            "reasons", Map.of(
                "flowerQuality", "43%",
                "deliveryIssue", "27%",
                "wrongItem", "18%",
                "customerError", "12%"
            ),
            "avgRefundAmount", 80.32,
            "refundsByStore", List.of(
                Map.of("store", "Montvale Main", "count", 8, "rate", "1.56%"),
                Map.of("store", "Glen Rock Branch", "count", 6, "rate", "1.36%"),
                Map.of("store", "Fair Lawn Express", "count", 5, "rate", "1.29%")
            )
        ));
    }

    // ── 21. 구독 지표 (ARR/MRR/Churn) ─────────────────────────
    @GetMapping("/subscription/metrics")
    public ApiResponse<?> getSubscriptionMetrics() {
        return ApiResponse.ok(Map.of(
            "activeSubscriptions", 287,
            "mrr", 17220.00,
            "arr", 206640.00,
            "churnRate", "3.2%",
            "avgSubscriptionLength", "8.5 months",
            "plans", Map.of(
                "weekly", Map.of("count", 89, "mrr", 5340.00),
                "biweekly", Map.of("count", 134, "mrr", 8040.00),
                "monthly", Map.of("count", 64, "mrr", 3840.00)
            ),
            "growthMoM", "+12.3%",
            "churnedLastMonth", 9,
            "newLastMonth", 41
        ));
    }

    // ── 22. 코호트 분석 ────────────────────────────────────────
    @GetMapping("/cohort")
    public ApiResponse<?> getCohortAnalysis() {
        List<Map<String, Object>> cohorts = List.of(
            Map.of("cohort", "2025-Q4", "size", 145, "month1", "100%", "month2", "45%", "month3", "32%", "month6", "21%"),
            Map.of("cohort", "2026-Q1", "size", 178, "month1", "100%", "month2", "48%", "month3", "35%", "month6", "N/A"),
            Map.of("cohort", "2026-Q2", "size", 201, "month1", "100%", "month2", "51%", "month3", "N/A", "month6", "N/A")
        );
        return ApiResponse.ok(Map.of("cohortRetention", cohorts, "insight", "Q2 cohort showing strongest 2-month retention (+13% vs Q4 2025)"));
    }

    // ── 23. 구매 깔때기 분석 ───────────────────────────────────
    @GetMapping("/funnel")
    public ApiResponse<?> getFunnelAnalysis(@RequestParam(defaultValue = "30") int days) {
        return ApiResponse.ok(Map.of(
            "period", days + " days",
            "funnel", List.of(
                Map.of("stage", "Homepage Visit", "users", 12450, "pct", "100%"),
                Map.of("stage", "Product View", "users", 7890, "pct", "63.4%"),
                Map.of("stage", "Add to Cart", "users", 3245, "pct", "26.1%"),
                Map.of("stage", "Checkout Start", "users", 1987, "pct", "16.0%"),
                Map.of("stage", "Order Complete", "users", 1654, "pct", "13.3%")
            ),
            "cartAbandonmentRate", "49.0%",
            "checkoutAbandonmentRate", "16.8%",
            "topDropOffReason", "Delivery fee surprise",
            "recommendation", "Add free delivery threshold badge on product pages"
        ));
    }

    // ── 24. AI 수요 예측 ───────────────────────────────────────
    @PostMapping("/forecast/demand")
    public ApiResponse<?> forecastDemand(@RequestBody Map<String, Object> body) {
        String storeId = (String) body.getOrDefault("storeId", "all");
        String horizon = body.getOrDefault("horizon", "7 days").toString();
        String contextNotes = (String) body.getOrDefault("notes", "");
        String prompt = "Flower shop demand forecast for store: " + storeId + ", horizon: " + horizon
            + ". Date: " + LocalDate.now() + ". Notes: " + contextNotes
            + ". Predict: total orders, top 5 products with units, revenue estimate, and inventory recommendation. Be concise with numbers.";
        String forecast = ollama.generate(prompt);
        if (forecast.isEmpty()) {
            forecast = "Forecast for " + horizon + ": ~" + (85 + rng.nextInt(30)) + " orders/day. Top products: Red Roses (45 units), Mixed Bouquets (38 units), Sunflowers (25 units). Revenue estimate: $6,200-7,800.";
        }
        return ApiResponse.ok(Map.of(
            "storeId", storeId, "horizon", horizon, "aiForecast", forecast,
            "confidence", "82%", "generatedAt", LocalDateTime.now().toString()
        ));
    }

    // ── 25. 종합 분석 대시보드 ─────────────────────────────────
    @GetMapping("/dashboard")
    public ApiResponse<?> getDashboard() {
        return ApiResponse.ok(Map.of(
            "summary", Map.of(
                "todayRevenue", 3247.50,
                "todayOrders", 42,
                "weekRevenue", 22180.00,
                "weekOrders", 287,
                "monthRevenue", 94500.00,
                "activeSubscriptions", 287,
                "pendingOrders", 8,
                "avgDeliveryTime", "48 min"
            ),
            "revenueGrowth", Map.of("dayOverDay", "+8.3%", "weekOverWeek", "+12.1%", "monthOverMonth", "+15.7%"),
            "topProducts", List.of("Red Roses", "Sunflowers", "Pink Peonies"),
            "alerts", List.of(
                Map.of("type", "LOW_STOCK", "message", "Red roses running low at Glen Rock (8 units left)", "severity", "HIGH"),
                Map.of("type", "LATE_DELIVERY", "message", "2 deliveries delayed in Fair Lawn zone", "severity", "MEDIUM")
            ),
            "generatedAt", LocalDateTime.now().toString()
        ));
    }
}
