-- PostgreSQL Performance Indexes for Jellyfin
-- Run this script to improve query performance
-- Safe to run multiple times (uses IF NOT EXISTS)
\pset pager off

\timing on
\echo 'Creating performance indexes for Jellyfin PostgreSQL database...'

-- ============================================================================
-- BaseItems Table Indexes
-- ============================================================================

\echo 'Creating indexes on BaseItems table...'

-- Index for filtering by type and virtual items (improves library filtering)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_type_isvirtual 
ON library."BaseItems" (Type, BaseItems.IsVirtualItem) 
WHERE IsVirtualItem = false;

-- Index for parent-child relationships (improves hierarchy queries)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_parent_type 
ON library."BaseItems" (BaseItems.ParentId, Type) 
WHERE ParentId IS NOT NULL;

-- Index for date-based queries (recently added items, sorting)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_datecreated 
ON library."BaseItems" (BaseItems.DateCreated DESC) 
WHERE DateCreated IS NOT NULL;

-- Index for date modified (sync operations)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_datemodified 
ON library."BaseItems" (BaseItems.DateModified DESC) 
WHERE DateModified IS NOT NULL;

-- Index for sorting by name (alphabetical browsing)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_sortname 
ON library."BaseItems" (BaseItems.SortName) 
WHERE SortName IS NOT NULL;

-- Index for TopParent lookups (library organization)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_topparent_type 
ON library."BaseItems" (BaseItems.TopParentId, Type) 
WHERE TopParentId IS NOT NULL;

-- Index for premiere date (upcoming/recent content)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_premieredate 
ON library."BaseItems" (BaseItems.PremiereDate DESC) 
WHERE PremiereDate IS NOT NULL;

-- Index for production year (decade/year filtering)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_productionyear 
ON library."BaseItems" (BaseItems.ProductionYear DESC) 
WHERE ProductionYear IS NOT NULL;

-- Index for community rating (sorting by rating)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_rating 
ON library."BaseItems" (BaseItems.CommunityRating DESC NULLS LAST);

-- Index for series episodes
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitems_series_episode 
ON library."BaseItems" (BaseItems.SeriesPresentationUniqueKey, BaseItems.IndexNumber, BaseItems.ParentIndexNumber) 
WHERE Type = 'MediaBrowser.Controller.Entities.TV.Episode';

-- ============================================================================
-- BaseItemProviders Table Indexes
-- ============================================================================

\echo 'Creating indexes on BaseItemProviders table...'

-- Index for provider value lookups (IMDb, TMDB, etc.)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitem_providers_value 
ON library."BaseItemProviders" (ProviderValue, ProviderId);

-- Index for finding all items by provider (reverse lookup)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_baseitem_providers_id 
ON library."BaseItemProviders" (ProviderId, ItemId);

-- ============================================================================
-- UserData Table Indexes
-- ============================================================================

\echo 'Creating indexes on UserData table...'

-- Index for finding user's watched items
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_userdata_user_played 
ON library."UserData" (UserId, Played);

-- Index for finding user's favorite items
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_userdata_user_favorite 
ON library."UserData" (UserId, IsFavorite) 
WHERE IsFavorite = true;

-- Index for last played date
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_userdata_lastplayed 
ON library."UserData" (LastPlayedDate DESC) 
WHERE LastPlayedDate IS NOT NULL;

-- ============================================================================
-- PeopleBaseItemMap Table Indexes
-- ============================================================================

\echo 'Creating indexes on PeopleBaseItemMap table...'

-- Index for finding all items for a person
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_people_items_person 
ON library."PeopleBaseItemMap" (PeopleId, ItemId);

-- Index for finding all people for an item (already covered by FK, but explicit)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_people_items_item 
ON library."PeopleBaseItemMap" (ItemId, PeopleId);

-- ============================================================================
-- ItemValuesMap Table Indexes  
-- ============================================================================

\echo 'Creating indexes on ItemValuesMap table...'

-- Index for finding items by value (genres, tags, etc.)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_itemvalues_map_value 
ON library."ItemValuesMap" (ItemValueId, ItemId);

-- ============================================================================
-- ItemValues Table Indexes (Critical Performance Optimization)
-- ============================================================================

\echo 'Creating indexes on ItemValues table...'

-- Index for CleanValue lookups (genre/tag searches by name)
-- Fixes: 1.3 billion row sequential scans
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_itemvalues_cleanvalue 
ON library."ItemValues" ("CleanValue") 
WHERE "CleanValue" IS NOT NULL;

-- Index for Value lookups (direct value searches)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_itemvalues_value 
ON library."ItemValues" ("Value") 
WHERE "Value" IS NOT NULL;

-- Index for ItemValueId + CleanValue (ItemValuesMap joins)
-- Improves genre/tag filtering performance
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_itemvalues_id_cleanvalue 
ON library."ItemValues" ("ItemValueId", "CleanValue");

-- ============================================================================
-- ActivityLog Table Indexes
-- ============================================================================

\echo 'Creating indexes on ActivityLog table...'

-- Index for date filtering (recent activity)
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_activitylog_date 
ON activitylog."ActivityLog" (DateCreated DESC);

-- Index for user activity
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_activitylog_user 
ON activitylog."ActivityLog" (UserId, DateCreated DESC) 
WHERE UserId IS NOT NULL;

-- ============================================================================
-- MediaStreamInfos Table Indexes
-- ============================================================================

\echo 'Creating indexes on MediaStreamInfos table...'

-- Index for finding streams by item and type
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_mediastreams_item_type 
ON library."MediaStreamInfos" (ItemId, Type);

-- Index for codec searches
CREATE INDEX CONCURRENTLY IF NOT EXISTS idx_mediastreams_codec 
ON library."MediaStreamInfos" (Codec) 
WHERE Codec IS NOT NULL;

-- ============================================================================
-- Analyze Tables
-- ============================================================================

\echo 'Analyzing tables to update statistics...'

ANALYZE library."BaseItems";
ANALYZE library."BaseItemProviders";
ANALYZE library."UserData";
ANALYZE library."PeopleBaseItemMap";
ANALYZE library."ItemValuesMap";
ANALYZE library."ItemValues";
ANALYZE library."MediaStreamInfos";

-- Analyze ActivityLog if it exists
DO $$
BEGIN
    IF EXISTS (
        SELECT 1 FROM information_schema.tables 
        WHERE table_schema = 'activitylog' 
        AND table_name = 'ActivityLog'
    ) THEN
        ANALYZE activitylog."ActivityLog";
        RAISE NOTICE 'ActivityLog analyzed';
    END IF;
END $$;

-- ============================================================================
-- Report Index Usage
-- ============================================================================

\echo 'Current index usage statistics:'

SELECT 
    schemaname,
    relname as tablename,
    indexrelname as indexname,
    pg_size_pretty(pg_relation_size(indexrelid)) as index_size,
    idx_scan as times_used,
    idx_tup_read as rows_read,
    idx_tup_fetch as rows_fetched
FROM pg_stat_user_indexes
WHERE schemaname IN ('library', 'activitylog', 'users', 'authentication', 'displaypreferences')
ORDER BY schemaname, relname, indexrelname;

\echo ''
\echo 'Index creation complete!'
\echo ''
\echo 'Note: CONCURRENTLY builds indexes without blocking writes, but takes longer.'
\echo 'Monitor progress with: SELECT * FROM pg_stat_progress_create_index;'
\echo ''
\echo 'After indexing, restart Jellyfin to clear connection pools.'
