-- ============================================
-- 优惠券效果分析 RPC 集合（Supabase / Postgres）
-- 说明：
-- - 这些函数只依赖业务域表：
--     coupon_templates, user_coupons, coupon_usage_logs, orders
-- - 不创建/修改业务表结构，由业务侧 schema 负责。
-- - 仅提供 Analytics Dashboard 所需的聚合统计。
-- - 调用方：/pages/mall/analytics/coupon-analysis.uvue
-- ============================================

-- 安全注意：
-- - 函数使用 SECURITY DEFINER，并将执行权限收敛到 authenticated 角色。
-- - 具体可见文件末尾的 REVOKE / GRANT 语句。


-- 1) 概览 KPI：发放/使用/GMV 提升/ROI/整体到期情况
CREATE OR REPLACE FUNCTION public.rpc_coupon_effectiveness_overview(
  p_start timestamptz,
  p_end   timestamptz,
  p_merchant_id uuid DEFAULT NULL
)
RETURNS TABLE (
  total_issued        numeric,
  total_used          numeric,
  usage_rate          numeric,
  gmv_increase        numeric,
  issued_growth       numeric,
  gmv_growth          numeric,
  roi                 numeric,
  about_to_expire_cnt numeric
)
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
  v_prev_start timestamptz;
  v_prev_end   timestamptz;
  v_prev_issued numeric := 0;
  v_prev_gmv    numeric := 0;
  v_discount_sum numeric := 0;
BEGIN
  IF p_start IS NULL OR p_end IS NULL OR p_start >= p_end THEN
    RAISE EXCEPTION 'invalid period';
  END IF;

  -- 上一周期窗口：长度与当前周期一致
  v_prev_start := p_start - (p_end - p_start);
  v_prev_end   := p_start;

  -- 当前周期：发放数量（user_coupons.received_at）
  SELECT
    COALESCE(COUNT(uc.id), 0)::numeric
  INTO total_issued
  FROM user_coupons uc
  JOIN coupon_templates ct ON ct.id = uc.template_id
  WHERE uc.received_at >= p_start
    AND uc.received_at < p_end
    AND (p_merchant_id IS NULL OR ct.merchant_id = p_merchant_id);

  -- 当前周期：使用记录 + GMV / 优惠额
  SELECT
    COALESCE(COUNT(cul.id), 0)::numeric,
    COALESCE(SUM(COALESCE(cul.order_amount, o.total_amount)), 0)::numeric,
    COALESCE(SUM(cul.discount_amount), 0)::numeric
  INTO total_used, gmv_increase, v_discount_sum
  FROM coupon_usage_logs cul
  JOIN coupon_templates ct ON ct.id = cul.template_id
  LEFT JOIN orders o ON o.id = cul.order_id
  WHERE cul.used_at >= p_start
    AND cul.used_at < p_end
    AND (p_merchant_id IS NULL OR ct.merchant_id = p_merchant_id);

  -- 使用率（0-100）
  IF total_issued > 0 THEN
    usage_rate := ROUND((total_used / total_issued) * 100.0, 2);
  ELSE
    usage_rate := 0;
  END IF;

  -- 上一周期：发放与 GMV，用于增长率
  SELECT
    COALESCE(COUNT(uc.id), 0)::numeric
  INTO v_prev_issued
  FROM user_coupons uc
  JOIN coupon_templates ct ON ct.id = uc.template_id
  WHERE uc.received_at >= v_prev_start
    AND uc.received_at < v_prev_end
    AND (p_merchant_id IS NULL OR ct.merchant_id = p_merchant_id);

  SELECT
    COALESCE(SUM(COALESCE(cul.order_amount, o.total_amount)), 0)::numeric
  INTO v_prev_gmv
  FROM coupon_usage_logs cul
  JOIN coupon_templates ct ON ct.id = cul.template_id
  LEFT JOIN orders o ON o.id = cul.order_id
  WHERE cul.used_at >= v_prev_start
    AND cul.used_at < v_prev_end
    AND (p_merchant_id IS NULL OR ct.merchant_id = p_merchant_id);

  -- 发放增长率
  IF v_prev_issued > 0 THEN
    issued_growth := ROUND(((total_issued - v_prev_issued) / v_prev_issued) * 100.0, 2);
  ELSE
    issued_growth := CASE WHEN total_issued > 0 THEN 100.0 ELSE 0.0 END;
  END IF;

  -- GMV 增长率
  IF v_prev_gmv > 0 THEN
    gmv_growth := ROUND(((gmv_increase - v_prev_gmv) / v_prev_gmv) * 100.0, 2);
  ELSE
    gmv_growth := CASE WHEN gmv_increase > 0 THEN 100.0 ELSE 0.0 END;
  END IF;

  -- ROI = (GMV_with_coupon - discount_sum) / discount_sum
  IF v_discount_sum > 0 THEN
    roi := ROUND(((gmv_increase - v_discount_sum) / v_discount_sum) * 100.0, 2);
  ELSE
    roi := 0;
  END IF;

  -- 未来 7 天内即将到期且未使用的券数量（整体概览）
  SELECT
    COALESCE(COUNT(uc2.id), 0)::numeric
  INTO about_to_expire_cnt
  FROM user_coupons uc2
  JOIN coupon_templates ct2 ON ct2.id = uc2.template_id
  WHERE uc2.status = 1          -- 假设 1 = unused（参考 mall.md）
    AND uc2.expire_at > now()
    AND uc2.expire_at <= now() + interval '7 days'
    AND (p_merchant_id IS NULL OR ct2.merchant_id = p_merchant_id);

  RETURN;
END;
$$;


-- 2) 按券类型统计：8 种券类型效果
CREATE OR REPLACE FUNCTION public.rpc_coupon_type_stats(
  p_start timestamptz,
  p_end   timestamptz,
  p_merchant_id uuid DEFAULT NULL
)
RETURNS TABLE (
  coupon_type     integer,
  total_issued    numeric,
  total_used      numeric,
  usage_rate      numeric,
  gmv_with_coupon numeric,
  amount_saved    numeric
)
LANGUAGE sql
SECURITY DEFINER
SET search_path = public
AS $$
  SELECT
    ct.coupon_type::integer                                AS coupon_type,
    COALESCE(COUNT(DISTINCT uc.id), 0)::numeric            AS total_issued,
    COALESCE(COUNT(DISTINCT cul.id), 0)::numeric           AS total_used,
    CASE
      WHEN COUNT(DISTINCT uc.id) > 0
        THEN ROUND(
          (COUNT(DISTINCT cul.id)::numeric / COUNT(DISTINCT uc.id)::numeric) * 100.0,
          2
        )
      ELSE 0
    END                                                    AS usage_rate,
    COALESCE(SUM(COALESCE(cul.order_amount, o.total_amount)), 0)::numeric AS gmv_with_coupon,
    COALESCE(SUM(cul.discount_amount), 0)::numeric         AS amount_saved
  FROM coupon_templates ct
  LEFT JOIN user_coupons uc
    ON uc.template_id = ct.id
   AND uc.received_at >= p_start
   AND uc.received_at <  p_end
  LEFT JOIN coupon_usage_logs cul
    ON cul.template_id = ct.id
   AND cul.used_at >= p_start
   AND cul.used_at <  p_end
  LEFT JOIN orders o
    ON o.id = cul.order_id
  WHERE (p_merchant_id IS NULL OR ct.merchant_id = p_merchant_id)
  GROUP BY ct.coupon_type
  ORDER BY total_issued DESC;
$$;


-- 3) 按发放渠道统计：主动领取/自动发放/活动/邀请/客服/积分
CREATE OR REPLACE FUNCTION public.rpc_coupon_channel_stats(
  p_start timestamptz,
  p_end   timestamptz,
  p_merchant_id uuid DEFAULT NULL
)
RETURNS TABLE (
  channel         text,
  total_issued    numeric,
  total_used      numeric,
  usage_rate      numeric,
  gmv_with_coupon numeric
)
LANGUAGE sql
SECURITY DEFINER
SET search_path = public
AS $$
  SELECT
    uc.obtain_channel::text                                AS channel,
    COALESCE(COUNT(DISTINCT uc.id), 0)::numeric            AS total_issued,
    COALESCE(COUNT(DISTINCT cul.id), 0)::numeric           AS total_used,
    CASE
      WHEN COUNT(DISTINCT uc.id) > 0
        THEN ROUND(
          (COUNT(DISTINCT cul.id)::numeric / COUNT(DISTINCT uc.id)::numeric) * 100.0,
          2
        )
      ELSE 0
    END                                                    AS usage_rate,
    COALESCE(SUM(COALESCE(cul.order_amount, o.total_amount)), 0)::numeric AS gmv_with_coupon
  FROM user_coupons uc
  JOIN coupon_templates ct
    ON ct.id = uc.template_id
  LEFT JOIN coupon_usage_logs cul
    ON cul.user_coupon_id = uc.id
   AND cul.used_at >= p_start
   AND cul.used_at <  p_end
  LEFT JOIN orders o
    ON o.id = cul.order_id
  WHERE uc.received_at >= p_start
    AND uc.received_at <  p_end
    AND (p_merchant_id IS NULL OR ct.merchant_id = p_merchant_id)
    AND uc.obtain_channel IS NOT NULL
  GROUP BY uc.obtain_channel
  ORDER BY total_issued DESC;
$$;


-- 4) 使用趋势：按天发放 vs 使用
CREATE OR REPLACE FUNCTION public.rpc_coupon_trend_daily(
  p_start timestamptz,
  p_end   timestamptz,
  p_merchant_id uuid DEFAULT NULL
)
RETURNS TABLE (
  day   date,
  issued numeric,
  used   numeric
)
LANGUAGE sql
SECURITY DEFINER
SET search_path = public
AS $$
  WITH days AS (
    SELECT generate_series(
      date_trunc('day', p_start)::date,
      date_trunc('day', p_end)::date,
      interval '1 day'
    )::date AS d
  ),
  issued AS (
    SELECT
      uc_day::date AS d,
      COUNT(*)::numeric AS cnt
    FROM (
      SELECT DATE(uc.received_at) AS uc_day
      FROM user_coupons uc
      JOIN coupon_templates ct ON ct.id = uc.template_id
      WHERE uc.received_at >= p_start
        AND uc.received_at <  p_end
        AND (p_merchant_id IS NULL OR ct.merchant_id = p_merchant_id)
    ) t
    GROUP BY uc_day
  ),
  used AS (
    SELECT
      cul_day::date AS d,
      COUNT(*)::numeric AS cnt
    FROM (
      SELECT DATE(cul.used_at) AS cul_day
      FROM coupon_usage_logs cul
      JOIN coupon_templates ct ON ct.id = cul.template_id
      WHERE cul.used_at >= p_start
        AND cul.used_at <  p_end
        AND (p_merchant_id IS NULL OR ct.merchant_id = p_merchant_id)
    ) t
    GROUP BY cul_day
  )
  SELECT
    d.d                                      AS day,
    COALESCE(i.cnt, 0)                       AS issued,
    COALESCE(u.cnt, 0)                       AS used
  FROM days d
  LEFT JOIN issued i ON i.d = d.d
  LEFT JOIN used   u ON u.d = d.d
  ORDER BY d.d;
$$;


-- 5) 转化效果：有券 vs 无券（GMV/订单数/客单价）
CREATE OR REPLACE FUNCTION public.rpc_coupon_conversion_effect(
  p_start timestamptz,
  p_end   timestamptz,
  p_merchant_id uuid DEFAULT NULL
)
RETURNS TABLE (
  metric         text,
  with_coupon    numeric,
  without_coupon numeric
)
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public
AS $$
DECLARE
  v_with_orders    numeric := 0;
  v_without_orders numeric := 0;
  v_with_gmv       numeric := 0;
  v_without_gmv    numeric := 0;
BEGIN
  -- 有券订单集合
  SELECT
    COALESCE(COUNT(DISTINCT o.id), 0)::numeric,
    COALESCE(SUM(o.total_amount), 0)::numeric
  INTO v_with_orders, v_with_gmv
  FROM orders o
  WHERE o.created_at >= p_start
    AND o.created_at <  p_end
    AND (p_merchant_id IS NULL OR o.merchant_id = p_merchant_id)
    AND EXISTS (
      SELECT 1
      FROM coupon_usage_logs cul
      WHERE cul.order_id = o.id
    );

  -- 无券订单集合
  SELECT
    COALESCE(COUNT(DISTINCT o.id), 0)::numeric,
    COALESCE(SUM(o.total_amount), 0)::numeric
  INTO v_without_orders, v_without_gmv
  FROM orders o
  WHERE o.created_at >= p_start
    AND o.created_at <  p_end
    AND (p_merchant_id IS NULL OR o.merchant_id = p_merchant_id)
    AND NOT EXISTS (
      SELECT 1
      FROM coupon_usage_logs cul
      WHERE cul.order_id = o.id
    );

  -- GMV 行
  metric := 'GMV';
  with_coupon := v_with_gmv;
  without_coupon := v_without_gmv;
  RETURN NEXT;

  -- 订单数 行
  metric := 'orders';
  with_coupon := v_with_orders;
  without_coupon := v_without_orders;
  RETURN NEXT;

  -- 客单价 行
  metric := 'avg_order_amount';
  with_coupon := CASE WHEN v_with_orders > 0 THEN ROUND(v_with_gmv / v_with_orders, 2) ELSE 0 END;
  without_coupon := CASE WHEN v_without_orders > 0 THEN ROUND(v_without_gmv / v_without_orders, 2) ELSE 0 END;
  RETURN NEXT;

  RETURN;
END;
$$;


-- ============================================
-- 权限收敛：只允许 authenticated 角色调用
-- ============================================

REVOKE ALL ON FUNCTION public.rpc_coupon_effectiveness_overview(timestamptz,timestamptz,uuid) FROM PUBLIC;
REVOKE ALL ON FUNCTION public.rpc_coupon_type_stats(timestamptz,timestamptz,uuid) FROM PUBLIC;
REVOKE ALL ON FUNCTION public.rpc_coupon_channel_stats(timestamptz,timestamptz,uuid) FROM PUBLIC;
REVOKE ALL ON FUNCTION public.rpc_coupon_trend_daily(timestamptz,timestamptz,uuid) FROM PUBLIC;
REVOKE ALL ON FUNCTION public.rpc_coupon_conversion_effect(timestamptz,timestamptz,uuid) FROM PUBLIC;

GRANT EXECUTE ON FUNCTION public.rpc_coupon_effectiveness_overview(timestamptz,timestamptz,uuid) TO authenticated;
GRANT EXECUTE ON FUNCTION public.rpc_coupon_type_stats(timestamptz,timestamptz,uuid) TO authenticated;
GRANT EXECUTE ON FUNCTION public.rpc_coupon_channel_stats(timestamptz,timestamptz,uuid) TO authenticated;
GRANT EXECUTE ON FUNCTION public.rpc_coupon_trend_daily(timestamptz,timestamptz,uuid) TO authenticated;
GRANT EXECUTE ON FUNCTION public.rpc_coupon_conversion_effect(timestamptz,timestamptz,uuid) TO authenticated;

-- 完成
SELECT 'Coupon analysis RPCs created successfully!' AS message;

