-- Description: Rebuilds daily product snapshots from scratch using real orders data.
--              Fixes issues with duplicated/inflated metrics.
-- Dependencies: Core import tables (products, orders, receivings).
-- Frequency: One-time run to clear out problematic data.

DO $$
DECLARE
    _module_name TEXT := 'rebuild_daily_snapshots';
    _start_time TIMESTAMPTZ := clock_timestamp();
    _date DATE;
    _count INT;
    _total_records INT := 0;
    _begin_date DATE := (SELECT MIN((date AT TIME ZONE 'America/Chicago'))::date FROM orders WHERE date >= '2020-01-01'); -- Starting point: captures all historical order data (business days, Central time)
    _end_date DATE := CURRENT_DATE;
BEGIN
    RAISE NOTICE 'Beginning daily snapshots rebuild from % to %. Starting at %', _begin_date, _end_date, _start_time;

    -- First truncate the existing snapshots to ensure a clean slate
    TRUNCATE TABLE public.daily_product_snapshots;
    RAISE NOTICE 'Cleared existing snapshot data';
    
    -- Now rebuild the snapshots day by day
    _date := _begin_date;
    
    WHILE _date <= _end_date LOOP
        RAISE NOTICE 'Processing date %...', _date;
        
        -- Create snapshots for this date
        WITH SalesData AS (
            SELECT
                p.pid,
                p.sku,
                -- Count orders to ensure we only include products with real activity
                COUNT(o.id) as order_count,
                -- Aggregate Sales (Quantity > 0, Status not Canceled/Returned/Combined)
                COALESCE(SUM(CASE WHEN o.quantity > 0 AND COALESCE(o.status, 'pending') NOT IN ('canceled', 'returned', 'combined') THEN o.quantity ELSE 0 END), 0) AS units_sold,
                COALESCE(SUM(CASE WHEN o.quantity > 0 AND COALESCE(o.status, 'pending') NOT IN ('canceled', 'returned', 'combined') THEN o.price * o.quantity ELSE 0 END), 0.00) AS gross_revenue_unadjusted,
                COALESCE(SUM(CASE WHEN o.quantity > 0 AND COALESCE(o.status, 'pending') NOT IN ('canceled', 'returned', 'combined') THEN o.discount ELSE 0 END), 0.00) AS discounts,
                COALESCE(SUM(CASE WHEN o.quantity > 0 AND COALESCE(o.status, 'pending') NOT IN ('canceled', 'returned', 'combined') THEN
                    COALESCE(
                        o.costeach,
                        get_weighted_avg_cost(p.pid, (o.date AT TIME ZONE 'America/Chicago')::date),
                        p.cost_price
                    ) * o.quantity
                ELSE 0 END), 0.00) AS cogs,
                COALESCE(SUM(CASE WHEN o.quantity > 0 AND COALESCE(o.status, 'pending') NOT IN ('canceled', 'returned', 'combined') THEN p.regular_price * o.quantity ELSE 0 END), 0.00) AS gross_regular_revenue,

                -- Aggregate Returns (Quantity < 0 or Status = Returned)
                COALESCE(SUM(CASE WHEN o.quantity < 0 OR COALESCE(o.status, 'pending') = 'returned' THEN ABS(o.quantity) ELSE 0 END), 0) AS units_returned,
                COALESCE(SUM(CASE WHEN o.quantity < 0 OR COALESCE(o.status, 'pending') = 'returned' THEN o.price * ABS(o.quantity) ELSE 0 END), 0.00) AS returns_revenue,
                -- Returns COGS: cost of returned goods offsets sales COGS
                COALESCE(SUM(CASE WHEN o.quantity < 0 OR COALESCE(o.status, 'pending') = 'returned' THEN
                    COALESCE(
                        o.costeach,
                        get_weighted_avg_cost(p.pid, (o.date AT TIME ZONE 'America/Chicago')::date),
                        p.cost_price
                    ) * ABS(o.quantity)
                ELSE 0 END), 0.00) AS returns_cogs
            FROM public.products p
            LEFT JOIN public.orders o
                ON p.pid = o.pid
               AND (o.date AT TIME ZONE 'America/Chicago')::date = _date -- business day (Central)
            GROUP BY p.pid, p.sku
            HAVING COUNT(o.id) > 0 -- Only include products with actual orders for this date
        ),
        ReceivingData AS (
            SELECT
                r.pid,
                -- Count receiving documents to ensure we only include products with real activity
                COUNT(DISTINCT r.receiving_id) as receiving_count,
                -- Calculate received quantity for this day
                SUM(r.qty_each) AS units_received,
                -- Calculate received cost for this day
                SUM(r.qty_each * r.cost_each) AS cost_received
            FROM public.receivings r
            WHERE (r.received_date AT TIME ZONE 'America/Chicago')::date = _date
            GROUP BY r.pid
            HAVING COUNT(DISTINCT r.receiving_id) > 0 OR SUM(r.qty_each) > 0
        ),
        -- Use historical stock from stock_snapshots when available,
        -- falling back to current stock from products table
        StockData AS (
            SELECT
                p.pid,
                COALESCE(ss.stock_quantity, p.stock_quantity) AS stock_quantity,
                COALESCE(ss.stock_value, p.stock_quantity * COALESCE(p.cost_price, 0.00)) AS stock_value,
                COALESCE(p.price, 0.00) as current_price,
                COALESCE(p.regular_price, 0.00) as current_regular_price
            FROM public.products p
            LEFT JOIN stock_snapshots ss ON p.pid = ss.pid AND ss.snapshot_date = _date
        )
        INSERT INTO public.daily_product_snapshots (
            snapshot_date,
            pid,
            sku,
            eod_stock_quantity,
            eod_stock_cost,
            eod_stock_retail,
            eod_stock_gross,
            stockout_flag,
            units_sold,
            units_returned,
            gross_revenue,
            discounts,
            returns_revenue,
            net_revenue,
            cogs,
            gross_regular_revenue,
            profit,
            units_received,
            cost_received,
            calculation_timestamp
        )
        SELECT
            _date AS snapshot_date,
            COALESCE(sd.pid, rd.pid) AS pid,
            sd.sku,
            -- Historical stock from stock_snapshots, falls back to current stock
            s.stock_quantity AS eod_stock_quantity,
            s.stock_value AS eod_stock_cost,
            s.stock_quantity * s.current_price AS eod_stock_retail,
            s.stock_quantity * s.current_regular_price AS eod_stock_gross,
            (s.stock_quantity <= 0) AS stockout_flag,
            -- Sales metrics
            COALESCE(sd.units_sold, 0),
            COALESCE(sd.units_returned, 0),
            COALESCE(sd.gross_revenue_unadjusted, 0.00),
            COALESCE(sd.discounts, 0.00),
            COALESCE(sd.returns_revenue, 0.00),
            COALESCE(sd.gross_revenue_unadjusted, 0.00) - COALESCE(sd.discounts, 0.00) - COALESCE(sd.returns_revenue, 0.00) AS net_revenue,
            COALESCE(sd.cogs, 0.00) - COALESCE(sd.returns_cogs, 0.00) AS cogs, -- net of returned goods' cost
            COALESCE(sd.gross_regular_revenue, 0.00),
            (COALESCE(sd.gross_revenue_unadjusted, 0.00) - COALESCE(sd.discounts, 0.00) - COALESCE(sd.returns_revenue, 0.00)) - (COALESCE(sd.cogs, 0.00) - COALESCE(sd.returns_cogs, 0.00)) AS profit,
            -- Receiving metrics
            COALESCE(rd.units_received, 0),
            COALESCE(rd.cost_received, 0.00),
            _start_time
        FROM SalesData sd
        FULL OUTER JOIN ReceivingData rd ON sd.pid = rd.pid
        LEFT JOIN StockData s ON COALESCE(sd.pid, rd.pid) = s.pid
        WHERE (COALESCE(sd.order_count, 0) > 0 OR COALESCE(rd.receiving_count, 0) > 0);
        
        -- Get record count for this day
        GET DIAGNOSTICS _count = ROW_COUNT;
        _total_records := _total_records + _count;
        
        RAISE NOTICE 'Added % snapshot records for date %', _count, _date;
        
        -- Move to next day
        _date := _date + INTERVAL '1 day';
    END LOOP;
    
    RAISE NOTICE 'Rebuilding daily snapshots complete. Added % total records across % days.', _total_records, (_end_date - _begin_date)::integer + 1;
    
    -- Update the status table for daily_snapshots
    INSERT INTO public.calculate_status (module_name, last_calculation_timestamp)
    VALUES ('daily_snapshots', _start_time)
    ON CONFLICT (module_name) DO UPDATE SET last_calculation_timestamp = _start_time;
    
    -- Now update product_metrics based on the rebuilt snapshots
    RAISE NOTICE 'Triggering update of product_metrics table...';
    
    -- Call the update_product_metrics procedure directly
    -- Your system might use a different method to trigger this update
    PERFORM pg_notify('recalculate_metrics', 'product_metrics');
    
    RAISE NOTICE 'Rebuild complete. Duration: %', clock_timestamp() - _start_time;
END $$; 