-- SESSION-TZ ASSUMPTION: this script runs with TimeZone=America/Chicago
-- (pinned in the pool config and as the inventory_db database default).
-- CURRENT_DATE / NOW()::date / ts::date here mean the BUSINESS date
-- (Chicago calendar day = 1am-Eastern day rule). See docs/TIME.md.
-- Description: Calculates and updates aggregated metrics per vendor.
-- Dependencies: product_metrics, products, purchase_orders, calculate_status table.
-- Frequency: Daily (after product_metrics update).

DO $$
DECLARE
    _module_name VARCHAR := 'vendor_metrics';
    _start_time TIMESTAMPTZ := clock_timestamp();
BEGIN
    RAISE NOTICE 'Running % calculation...', _module_name;

    WITH VendorProductAggregates AS (
        -- Aggregate metrics from product_metrics table per vendor
        SELECT
            p.vendor,
            COUNT(DISTINCT pm.pid) AS product_count,
            COUNT(DISTINCT CASE WHEN pm.is_visible THEN pm.pid END) AS active_product_count,
            COUNT(DISTINCT CASE WHEN pm.is_replenishable THEN pm.pid END) AS replenishable_product_count,
            SUM(pm.current_stock) AS current_stock_units,
            SUM(pm.current_stock_cost) AS current_stock_cost,
            SUM(pm.current_stock_retail) AS current_stock_retail,
            SUM(pm.on_order_qty) AS on_order_units,
            SUM(pm.on_order_cost) AS on_order_cost,
            -- Only include products with valid sales data in each time period
            COUNT(DISTINCT CASE WHEN pm.sales_7d > 0 THEN pm.pid END) AS products_with_sales_7d,
            SUM(CASE WHEN pm.sales_7d > 0 THEN pm.sales_7d ELSE 0 END) AS sales_7d,
            SUM(COALESCE(pm.revenue_7d, 0)) AS revenue_7d,
            
            COUNT(DISTINCT CASE WHEN pm.sales_30d > 0 THEN pm.pid END) AS products_with_sales_30d,
            SUM(CASE WHEN pm.sales_30d > 0 THEN pm.sales_30d ELSE 0 END) AS sales_30d,
            SUM(COALESCE(pm.revenue_30d, 0)) AS revenue_30d,
            SUM(COALESCE(pm.cogs_30d, 0)) AS cogs_30d,
            SUM(COALESCE(pm.profit_30d, 0)) AS profit_30d,
            
            COUNT(DISTINCT CASE WHEN pm.sales_365d > 0 THEN pm.pid END) AS products_with_sales_365d,
            SUM(CASE WHEN pm.sales_365d > 0 THEN pm.sales_365d ELSE 0 END) AS sales_365d,
            SUM(COALESCE(pm.revenue_365d, 0)) AS revenue_365d,
            
            COUNT(DISTINCT CASE WHEN pm.lifetime_sales > 0 THEN pm.pid END) AS products_with_lifetime_sales,
            SUM(CASE WHEN pm.lifetime_sales > 0 THEN pm.lifetime_sales ELSE 0 END) AS lifetime_sales,
            SUM(COALESCE(pm.lifetime_revenue, 0)) AS lifetime_revenue
        FROM public.product_metrics pm
        JOIN public.products p ON pm.pid = p.pid
        WHERE p.vendor IS NOT NULL AND p.vendor <> ''
        GROUP BY p.vendor
    ),
    PreviousPeriodVendorMetrics AS (
        -- Get previous period metrics for growth calculation
        SELECT
            p.vendor,
            SUM(CASE WHEN dps.snapshot_date >= CURRENT_DATE - INTERVAL '59 days' 
                     AND dps.snapshot_date < CURRENT_DATE - INTERVAL '29 days' 
                THEN dps.units_sold ELSE 0 END) AS sales_prev_30d,
            SUM(CASE WHEN dps.snapshot_date >= CURRENT_DATE - INTERVAL '59 days' 
                     AND dps.snapshot_date < CURRENT_DATE - INTERVAL '29 days' 
                THEN dps.net_revenue ELSE 0 END) AS revenue_prev_30d
        FROM public.daily_product_snapshots dps
        JOIN public.products p ON dps.pid = p.pid
        WHERE p.vendor IS NOT NULL AND p.vendor <> ''
        GROUP BY p.vendor
    ),
    VendorPOAggregates AS (
        -- Lead time per PO line = days to its FIRST receiving from the same supplier
        -- (within 180 days), then averaged per vendor. Joining each PO line to EVERY
        -- later receiving overstated lead time and weighted it toward busy products.
        -- Same shape as the per-product calc in update_periodic_metrics.sql.
        SELECT
            vendor,
            COUNT(DISTINCT po_id) AS po_count_365d,
            ROUND(AVG(GREATEST(1, first_receive_date - po_date)))::int AS avg_lead_time_days_hist
        FROM (
            SELECT
                po.vendor,
                po.po_id,
                po.pid,
                po.date::date AS po_date,
                MIN(r.received_date::date) AS first_receive_date
            FROM public.purchase_orders po
            JOIN public.receivings r ON r.pid = po.pid AND r.supplier_id = po.supplier_id
                AND r.received_date >= po.date
                AND r.received_date <= po.date + INTERVAL '180 days'
            WHERE po.status = 'done'
              AND po.date >= CURRENT_DATE - INTERVAL '1 year'
              AND po.vendor IS NOT NULL AND po.vendor <> ''
            GROUP BY po.vendor, po.po_id, po.pid, po.date
        ) po_first_receiving
        GROUP BY vendor
    ),
    AllVendors AS (
        -- Ensure all vendors from products table are included
        SELECT DISTINCT vendor FROM public.products WHERE vendor IS NOT NULL AND vendor <> ''
    )
    INSERT INTO public.vendor_metrics (
        vendor_name, last_calculated,
        product_count, active_product_count, replenishable_product_count,
        current_stock_units, current_stock_cost, current_stock_retail,
        on_order_units, on_order_cost,
        po_count_365d, avg_lead_time_days,
        sales_7d, revenue_7d, sales_30d, revenue_30d, profit_30d, cogs_30d,
        sales_365d, revenue_365d, lifetime_sales, lifetime_revenue,
        avg_margin_30d,
        sales_growth_30d_vs_prev, revenue_growth_30d_vs_prev
    )
    SELECT
        v.vendor,
        _start_time,
        -- Base Aggregates
        COALESCE(vpa.product_count, 0),
        COALESCE(vpa.active_product_count, 0),
        COALESCE(vpa.replenishable_product_count, 0),
        COALESCE(vpa.current_stock_units, 0),
        COALESCE(vpa.current_stock_cost, 0.00),
        COALESCE(vpa.current_stock_retail, 0.00),
        COALESCE(vpa.on_order_units, 0),
        COALESCE(vpa.on_order_cost, 0.00),
        -- PO Aggregates
        COALESCE(vpoa.po_count_365d, 0),
        vpoa.avg_lead_time_days_hist, -- Can be NULL if no received POs
        -- Sales Aggregates
        COALESCE(vpa.sales_7d, 0), COALESCE(vpa.revenue_7d, 0.00),
        COALESCE(vpa.sales_30d, 0), COALESCE(vpa.revenue_30d, 0.00),
        COALESCE(vpa.profit_30d, 0.00), COALESCE(vpa.cogs_30d, 0.00),
        COALESCE(vpa.sales_365d, 0), COALESCE(vpa.revenue_365d, 0.00),
        COALESCE(vpa.lifetime_sales, 0), COALESCE(vpa.lifetime_revenue, 0.00),
        -- KPIs
        (vpa.profit_30d / NULLIF(vpa.revenue_30d, 0)) * 100.0,
        -- Growth metrics
        std_numeric(safe_divide((vpa.sales_30d - ppvm.sales_prev_30d) * 100.0, ppvm.sales_prev_30d), 2),
        std_numeric(safe_divide((vpa.revenue_30d - ppvm.revenue_prev_30d) * 100.0, ppvm.revenue_prev_30d), 2)
    FROM AllVendors v
    LEFT JOIN VendorProductAggregates vpa ON v.vendor = vpa.vendor
    LEFT JOIN VendorPOAggregates vpoa ON v.vendor = vpoa.vendor
    LEFT JOIN PreviousPeriodVendorMetrics ppvm ON v.vendor = ppvm.vendor

    ON CONFLICT (vendor_name) DO UPDATE SET
        last_calculated = EXCLUDED.last_calculated,
        product_count = EXCLUDED.product_count,
        active_product_count = EXCLUDED.active_product_count,
        replenishable_product_count = EXCLUDED.replenishable_product_count,
        current_stock_units = EXCLUDED.current_stock_units,
        current_stock_cost = EXCLUDED.current_stock_cost,
        current_stock_retail = EXCLUDED.current_stock_retail,
        on_order_units = EXCLUDED.on_order_units,
        on_order_cost = EXCLUDED.on_order_cost,
        po_count_365d = EXCLUDED.po_count_365d,
        avg_lead_time_days = EXCLUDED.avg_lead_time_days,
        sales_7d = EXCLUDED.sales_7d, revenue_7d = EXCLUDED.revenue_7d,
        sales_30d = EXCLUDED.sales_30d, revenue_30d = EXCLUDED.revenue_30d,
        profit_30d = EXCLUDED.profit_30d, cogs_30d = EXCLUDED.cogs_30d,
        sales_365d = EXCLUDED.sales_365d, revenue_365d = EXCLUDED.revenue_365d,
        lifetime_sales = EXCLUDED.lifetime_sales, lifetime_revenue = EXCLUDED.lifetime_revenue,
        avg_margin_30d = EXCLUDED.avg_margin_30d,
        sales_growth_30d_vs_prev = EXCLUDED.sales_growth_30d_vs_prev,
        revenue_growth_30d_vs_prev = EXCLUDED.revenue_growth_30d_vs_prev
    WHERE -- Only update if at least one value has changed
        vendor_metrics.product_count IS DISTINCT FROM EXCLUDED.product_count OR
        vendor_metrics.active_product_count IS DISTINCT FROM EXCLUDED.active_product_count OR
        vendor_metrics.current_stock_units IS DISTINCT FROM EXCLUDED.current_stock_units OR
        vendor_metrics.on_order_units IS DISTINCT FROM EXCLUDED.on_order_units OR
        vendor_metrics.sales_30d IS DISTINCT FROM EXCLUDED.sales_30d OR
        vendor_metrics.revenue_30d IS DISTINCT FROM EXCLUDED.revenue_30d OR
        vendor_metrics.lifetime_sales IS DISTINCT FROM EXCLUDED.lifetime_sales OR
        -- Cost revisions can change profit/cogs with unchanged sales/revenue
        vendor_metrics.profit_30d IS DISTINCT FROM EXCLUDED.profit_30d OR
        vendor_metrics.cogs_30d IS DISTINCT FROM EXCLUDED.cogs_30d OR
        vendor_metrics.avg_lead_time_days IS DISTINCT FROM EXCLUDED.avg_lead_time_days;

    -- Update calculate_status
    INSERT INTO public.calculate_status (module_name, last_calculation_timestamp)
    VALUES (_module_name, _start_time)
    ON CONFLICT (module_name) DO UPDATE SET last_calculation_timestamp = _start_time;

    RAISE NOTICE 'Finished % calculation. Duration: %', _module_name, clock_timestamp() - _start_time;
END $$;

-- Return metrics about the update operation for tracking
WITH update_stats AS (
    SELECT 
        COUNT(*) as total_vendors,
        COUNT(*) FILTER (WHERE last_calculated >= NOW() - INTERVAL '5 minutes') as rows_processed,
        SUM(product_count) as total_products,
        SUM(active_product_count) as total_active_products,
        SUM(po_count_365d) as total_pos_365d,
        AVG(avg_lead_time_days) as overall_avg_lead_time
    FROM public.vendor_metrics
)
SELECT 
    rows_processed,
    total_vendors,
    total_products::int,
    total_active_products::int,
    total_pos_365d::int,
    ROUND(overall_avg_lead_time, 1) as overall_avg_lead_time
FROM update_stats;