-- ============================================================
-- Phase 10: SKU 物化视图 — 按月预计算 fn_dish_sku_summary / fn_dish_sku_abc
-- 解决：fn_dish_sku_summary 实时计算需 80+ 秒，物化后毫秒级
-- 用法：数据导入后执行 REFRESH MATERIALIZED VIEW 或调用刷新函数
-- ============================================================

-- 1. mv_dish_sku_summary_monthly — 按月物化菜品SKU汇总
DROP MATERIALIZED VIEW IF EXISTS analytics.mv_dish_sku_summary_monthly;
CREATE MATERIALIZED VIEW analytics.mv_dish_sku_summary_monthly AS
SELECT
  date_trunc('month', b.closed_at)::date AS month_start,
  d.dish_name,
  min(d.category_level1) AS category_level1,
  min(d.category_level2) AS category_level2,
  count(*) AS detail_rows,
  count(DISTINCT d.store_code || '|' || d.bill_no) AS bill_count,
  count(DISTINCT d.store_code) AS store_count,
  sum(d.sales_quantity) AS sales_quantity,
  sum(d.gross_amount) AS gross_amount,
  sum(d.received_amount) AS received_amount,
  sum(d.gross_amount - d.received_amount) AS discount_amount,
  round(sum(d.gross_amount - d.received_amount) / NULLIF(sum(d.gross_amount), 0) * 100, 2) AS discount_rate_pct,
  round(sum(d.received_amount) / NULLIF(sum(d.sales_quantity), 0), 2) AS realized_unit_price
FROM public.dish_sales_details d
  JOIN analytics.bill_fact b ON b.store_code = d.store_code AND b.bill_no = d.bill_no
WHERE d.dish_name IS NOT NULL
GROUP BY date_trunc('month', b.closed_at)::date, d.dish_name;

CREATE UNIQUE INDEX idx_mv_sku_summary_month_dish ON analytics.mv_dish_sku_summary_monthly (month_start, dish_name);
CREATE INDEX idx_mv_sku_summary_month ON analytics.mv_dish_sku_summary_monthly (month_start);

-- 2. mv_dish_sku_abc_monthly — 按月物化ABC分类
DROP MATERIALIZED VIEW IF EXISTS analytics.mv_dish_sku_abc_monthly;
CREATE MATERIALIZED VIEW analytics.mv_dish_sku_abc_monthly AS
WITH medians AS (
  SELECT
    month_start,
    percentile_cont(0.5) WITHIN GROUP (ORDER BY sales_quantity::double precision) AS median_quantity,
    percentile_cont(0.5) WITHIN GROUP (ORDER BY received_amount::double precision) AS median_revenue
  FROM analytics.mv_dish_sku_summary_monthly
  GROUP BY month_start
),
ranked AS (
  SELECT
    s.*,
    sum(s.received_amount) OVER (PARTITION BY s.month_start ORDER BY s.received_amount DESC, s.dish_name ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
      / NULLIF(sum(s.received_amount) OVER (PARTITION BY s.month_start), 0) AS cumulative_revenue_share,
    m.median_quantity,
    m.median_revenue
  FROM analytics.mv_dish_sku_summary_monthly s
  JOIN medians m ON s.month_start = m.month_start
)
SELECT
  month_start,
  dish_name,
  category_level1,
  category_level2,
  detail_rows,
  bill_count,
  store_count,
  sales_quantity,
  gross_amount,
  received_amount,
  discount_amount,
  discount_rate_pct,
  realized_unit_price,
  round(received_amount / NULLIF(sum(received_amount) OVER (PARTITION BY month_start), 0) * 100, 4) AS revenue_share_pct,
  cumulative_revenue_share,
  median_quantity,
  median_revenue,
  CASE
    WHEN cumulative_revenue_share <= 0.70 THEN 'A-核心'
    WHEN cumulative_revenue_share <= 0.90 THEN 'B-成长'
    ELSE 'C-长尾'
  END AS abc_class,
  CASE
    WHEN sales_quantity::double precision >= median_quantity AND received_amount::double precision >= median_revenue THEN '明星菜品'
    WHEN sales_quantity::double precision >= median_quantity AND received_amount::double precision < median_revenue THEN '引流菜品'
    WHEN sales_quantity::double precision < median_quantity AND received_amount::double precision >= median_revenue THEN '潜力菜品'
    ELSE '淘汰观察品'
  END AS sales_quadrant
FROM ranked;

CREATE UNIQUE INDEX idx_mv_sku_abc_month_dish ON analytics.mv_dish_sku_abc_monthly (month_start, dish_name);
CREATE INDEX idx_mv_sku_abc_month ON analytics.mv_dish_sku_abc_monthly (month_start);
CREATE INDEX idx_mv_sku_abc_class ON analytics.mv_dish_sku_abc_monthly (month_start, abc_class);

-- 3. 刷新函数 — 数据导入后调用
CREATE OR REPLACE FUNCTION analytics.refresh_sku_materialized_views(p_month date DEFAULT NULL)
RETURNS void
LANGUAGE plpgsql
AS $function$
BEGIN
  IF p_month IS NULL THEN
    REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_summary_monthly;
    REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_abc_monthly;
  ELSE
    -- 全量刷新（物化视图不支持增量，但数据量小刷新快）
    REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_summary_monthly;
    REFRESH MATERIALIZED VIEW analytics.mv_dish_sku_abc_monthly;
  END IF;
END;
$function$;
