-- Migration: Optimize weekly_report_mv by denormalizing created_date into nodes and excluding current week from MV
-- Date: 2026-01-26
--
-- This migration:
-- 1. Adds created_date column to nodes table (denormalization to avoid JOIN with entries)
-- 2. Populates existing data from entries
-- 3. Creates indexes for optimized queries
-- 4. Updates MV to exclude current week and use nodes.created_date instead of entries.created_date

-- ============================================
-- Step 1: Add created_date column to nodes
-- ============================================
ALTER TABLE nodes 
ADD COLUMN created_date TIMESTAMP WITH TIME ZONE;

-- ============================================
-- Step 2: Populate existing data from entries
-- ============================================
UPDATE nodes n 
SET created_date = e.created_date 
FROM entries e 
WHERE n.entry_id = e.id;

-- ============================================
-- Step 3: Set NOT NULL constraint
-- ============================================
ALTER TABLE nodes 
ALTER COLUMN created_date SET NOT NULL;

-- ============================================
-- Step 4: Create indexes for optimized queries
-- ============================================
-- Index for filtering by date and user (for current week queries)
CREATE INDEX IF NOT EXISTS idx_nodes_created_date_user 
ON nodes(created_date, user_id);

-- Index for queries with grouping by project (for current week queries)
CREATE INDEX IF NOT EXISTS idx_nodes_project_user_created_date 
ON nodes(project_id, user_id, created_date);

COMMENT ON INDEX idx_nodes_created_date_user IS 'Index for filtering nodes by created_date and user_id - optimized for current week queries';
COMMENT ON INDEX idx_nodes_project_user_created_date IS 'Index for grouping nodes by project, user and created_date - optimized for current week aggregation queries';

-- ============================================
-- Step 5: Recreate MV to exclude current week and use nodes.created_date
-- ============================================
DROP MATERIALIZED VIEW IF EXISTS weekly_report_mv;

CREATE MATERIALIZED VIEW weekly_report_mv AS
SELECT
    p.id AS project_id,
    agg.report_year,
    agg.report_week,
    COALESCE(agg.total_score, 0.0000) AS total_score,
    CASE
        WHEN wg.max_score IS NULL THEN COALESCE(agg.total_score, 0.0000)
        ELSE LEAST(COALESCE(agg.total_score, 0.0000), wg.max_score)
    END AS normalized_total_score
FROM
    projects p
LEFT JOIN 
    (
        SELECT
            n.project_id,
            EXTRACT(ISOYEAR FROM n.created_date)::INTEGER AS report_year,
            EXTRACT(WEEK FROM n.created_date)::INTEGER AS report_week,
            SUM(n.score) AS total_score
        FROM
            nodes n
        WHERE
            -- Exclude current week: only include data from previous weeks
            (EXTRACT(ISOYEAR FROM n.created_date)::INTEGER < EXTRACT(ISOYEAR FROM CURRENT_DATE)::INTEGER)
            OR (EXTRACT(ISOYEAR FROM n.created_date)::INTEGER = EXTRACT(ISOYEAR FROM CURRENT_DATE)::INTEGER 
                AND EXTRACT(WEEK FROM n.created_date)::INTEGER < EXTRACT(WEEK FROM CURRENT_DATE)::INTEGER)
        GROUP BY
            1, 2, 3
    ) agg 
    ON p.id = agg.project_id
LEFT JOIN
    weekly_goals wg
    ON wg.project_id = p.id
    AND wg.goal_year = agg.report_year
    AND wg.goal_week = agg.report_week
WHERE
    p.deleted = FALSE
ORDER BY
    p.id, agg.report_year, agg.report_week
WITH DATA;

-- Recreate index on MV
CREATE INDEX idx_weekly_report_mv_project_year_week 
ON weekly_report_mv(project_id, report_year, report_week);

COMMENT ON MATERIALIZED VIEW weekly_report_mv IS 'Materialized view aggregating weekly scores by project using ISOYEAR for correct week calculations at year boundaries. Includes all projects via LEFT JOIN. Adds normalized_total_score using weekly_goals.max_score snapshot. Contains only historical data (excludes current week). Uses nodes.created_date (denormalized) instead of entries.created_date.';
