-- Feature 2.2 — Line Movement + Cascade Detection -- New tables: line_baselines, line_movements, cascade_alerts -- ============================================================ -- TABLE: line_baselines -- Morning baseline lines for movement detection -- ============================================================ CREATE TABLE public.line_baselines ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), sport TEXT NOT NULL, game_date DATE NOT NULL, player TEXT NOT NULL, stat_type TEXT NOT NULL, book TEXT NOT NULL, baseline_line NUMERIC(5,1) NOT NULL, captured_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE UNIQUE INDEX idx_baseline_unique ON public.line_baselines(game_date, player, stat_type, book); CREATE INDEX idx_baseline_date ON public.line_baselines(game_date); -- No RLS needed — server-only table, not user-facing -- ============================================================ -- TABLE: line_movements -- Detected line movements throughout the day -- ============================================================ CREATE TABLE public.line_movements ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), sport TEXT NOT NULL, game_date DATE NOT NULL, player TEXT NOT NULL, stat_type TEXT NOT NULL, book TEXT NOT NULL, baseline_line NUMERIC(5,1) NOT NULL, current_line NUMERIC(5,1) NOT NULL, movement NUMERIC(4,1) NOT NULL, direction TEXT NOT NULL CHECK (direction IN ('up', 'down')), sharp_indicator TEXT CHECK (sharp_indicator IN ('sharp_action', 'public_action', 'unknown')), detected_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_movements_date ON public.line_movements(game_date); CREATE INDEX idx_movements_player ON public.line_movements(player); -- No RLS needed — server-only table -- ============================================================ -- TABLE: cascade_alerts -- Alerts for users whose scans are affected -- ============================================================ CREATE TABLE public.cascade_alerts ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES public.users(id) ON DELETE CASCADE, alert_type TEXT NOT NULL CHECK (alert_type IN ('player_scratched', 'line_moved', 'parlay_regraded')), scan_session_id UUID REFERENCES public.scan_sessions(id), player TEXT, detail TEXT NOT NULL, is_read BOOLEAN NOT NULL DEFAULT false, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); CREATE INDEX idx_alerts_user ON public.cascade_alerts(user_id, is_read); ALTER TABLE public.cascade_alerts ENABLE ROW LEVEL SECURITY; CREATE POLICY "alerts_select_own" ON public.cascade_alerts FOR SELECT USING (auth.uid() = user_id); CREATE POLICY "alerts_update_own" ON public.cascade_alerts FOR UPDATE USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id); -- Cleanup: delete baselines older than 2 days (call periodically) CREATE OR REPLACE FUNCTION public.cleanup_old_baselines() RETURNS void AS $$ BEGIN DELETE FROM public.line_baselines WHERE game_date < (CURRENT_DATE - INTERVAL '2 days'); DELETE FROM public.line_movements WHERE game_date < (CURRENT_DATE - INTERVAL '2 days'); END; $$ LANGUAGE plpgsql SECURITY DEFINER;