-- PostgreSQL Performance Diagnostics for Jellyfin
-- Run this to diagnose current performance issues
\pset pager off

\timing on
\x auto

\echo '========================================='
\echo 'Jellyfin PostgreSQL Performance Report'
\echo '========================================='
\echo ''

-- ============================================================================
-- 1. Connection Pool Status
-- ============================================================================

\echo '1. CONNECTION POOL STATUS'
\echo '-----------------------------------------'

SELECT
    count(*) as total_connections,
    count(*) FILTER (WHERE state = 'active') as active,
    count(*) FILTER (WHERE state = 'idle') as idle,
    count(*) FILTER (WHERE state = 'idle in transaction') as idle_in_transaction,
    count(*) FILTER (WHERE state = 'idle in transaction (aborted)') as aborted,
    count(*) FILTER (WHERE wait_event_type IS NOT NULL) as waiting
FROM pg_stat_activity
WHERE datname = 'jellyfin';

\echo ''

-- ============================================================================
-- 2. Long-Running Queries
-- ============================================================================

\echo '2. LONG-RUNNING QUERIES (>1 second)'
\echo '-----------------------------------------'

SELECT
    pid,
    usename,
    application_name,
    now() - query_start as duration,
    state,
    wait_event_type,
    wait_event,
    left(query, 100) as query_preview
FROM pg_stat_activity
WHERE datname = 'jellyfin'
  AND state != 'idle'
  AND query_start < now() - interval '1 second'
ORDER BY duration DESC
LIMIT 10;

\echo ''

-- ============================================================================
-- 3. Table Sizes
-- ============================================================================

\echo '3. TABLE SIZES (Top 10)'
\echo '-----------------------------------------'

SELECT
    s.schemaname,
    s.relname AS tablename,
    pg_size_pretty(pg_total_relation_size(fqname)) AS total_size,
    pg_size_pretty(pg_relation_size(fqname)) AS table_size,
    pg_size_pretty(pg_total_relation_size(fqname) - pg_relation_size(fqname)) AS index_size,
    s.n_live_tup AS row_count
FROM (
    SELECT
        s.*,
        quote_ident(s.schemaname) || '.' || quote_ident(s.relname) AS fqname
    FROM pg_stat_user_tables s
) s
WHERE s.schemaname IN ('library', 'users', 'authentication', 'displaypreferences', 'activitylog')
ORDER BY pg_total_relation_size(fqname) DESC
LIMIT 10;

\echo ''

-- ============================================================================
-- 4. Index Usage Statistics
-- ============================================================================

\echo '4. INDEX USAGE (Tables with low index usage)'
\echo '-----------------------------------------'

SELECT
    schemaname,
    relname as tablename,
    seq_scan,
    idx_scan,
    n_live_tup as rows,
    CASE
        WHEN seq_scan + idx_scan > 0
        THEN round(idx_scan::numeric / (seq_scan + idx_scan) * 100, 2)
        ELSE 0
    END as index_usage_percent
FROM pg_stat_user_tables
WHERE schemaname IN ('library', 'users', 'authentication', 'displaypreferences', 'activitylog')
  AND n_live_tup > 100
ORDER BY
    CASE
        WHEN seq_scan + idx_scan > 0
        THEN idx_scan::numeric / (seq_scan + idx_scan)
        ELSE 0
    END ASC
LIMIT 10;

\echo ''

-- ============================================================================
-- 5. Missing Indexes (High Sequential Scans)
-- ============================================================================

\echo '5. TABLES WITH HIGH SEQUENTIAL SCANS'
\echo '-----------------------------------------'

SELECT
    schemaname,
    relname as tablename,
    seq_scan,
    seq_tup_read,
    idx_scan,
    n_live_tup,
    CASE
        WHEN seq_scan > 0
        THEN seq_tup_read / seq_scan
        ELSE 0
    END as avg_rows_per_seq_scan
FROM pg_stat_user_tables
WHERE schemaname IN ('library', 'users', 'authentication', 'displaypreferences', 'activitylog')
  AND seq_scan > 100
  AND seq_tup_read > 10000
ORDER BY seq_tup_read DESC
LIMIT 10;

\echo ''

-- ============================================================================
-- 6. Table Bloat (Dead Tuples)
-- ============================================================================

\echo '6. TABLE BLOAT (Dead Tuples)'
\echo '-----------------------------------------'

SELECT
    schemaname,
    relname as tablename,
    n_dead_tup as dead_tuples,
    n_live_tup as live_tuples,
    CASE
        WHEN n_live_tup > 0
        THEN round(n_dead_tup::numeric / n_live_tup * 100, 2)
        ELSE 0
    END as dead_tuple_percent,
    last_vacuum,
    last_autovacuum,
    last_analyze,
    last_autoanalyze
FROM pg_stat_user_tables
WHERE schemaname IN ('library', 'users', 'authentication', 'displaypreferences', 'activitylog')
  AND n_dead_tup > 100
ORDER BY n_dead_tup DESC
LIMIT 10;

\echo ''

-- ============================================================================
-- 7. Cache Hit Ratio
-- ============================================================================

\echo '7. CACHE HIT RATIO (Should be >95%)'
\echo '-----------------------------------------'

SELECT
    'Buffer Cache Hit Ratio' as metric,
    round(
        sum(blks_hit) / nullif(sum(blks_hit) + sum(blks_read), 0) * 100,
        2
    ) as hit_ratio_percent
FROM pg_stat_database
WHERE datname = 'jellyfin';

\echo ''

-- ============================================================================
-- 8. Index Efficiency
-- ============================================================================

\echo '8. UNUSED OR RARELY USED INDEXES'
\echo '-----------------------------------------'

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,
    idx_tup_fetch
FROM pg_stat_user_indexes
WHERE schemaname IN ('library', 'users', 'authentication', 'displaypreferences', 'activitylog')
  AND idx_scan < 50
  AND pg_relation_size(indexrelid) > 1000000
ORDER BY tablename, pg_relation_size(indexrelid) DESC;
--LIMIT 10;

\echo ''

-- ============================================================================
-- 9. Locks
-- ============================================================================

\echo '9. CURRENT LOCKS (Blocked queries)'
\echo '-----------------------------------------'

SELECT
    bl.pid AS blocked_pid,
    a.usename AS blocked_user,
    ka.query AS blocking_query,
    now() - ka.query_start AS blocking_duration,
    a.query AS blocked_query
FROM pg_catalog.pg_locks bl
JOIN pg_catalog.pg_stat_activity a ON bl.pid = a.pid
JOIN pg_catalog.pg_locks kl ON bl.transactionid = kl.transactionid AND bl.pid != kl.pid
JOIN pg_catalog.pg_stat_activity ka ON kl.pid = ka.pid
WHERE NOT bl.granted
  AND a.datname = 'jellyfin';

\echo ''

-- ============================================================================
-- 10. Slow Queries (if pg_stat_statements is enabled)
-- ============================================================================

\echo '10. SLOWEST QUERIES (Requires pg_stat_statements extension)'
\echo '-----------------------------------------'

DO $$
BEGIN
    IF EXISTS (
        SELECT 1 FROM pg_extension WHERE extname = 'pg_stat_statements'
    ) THEN
        RAISE NOTICE 'pg_stat_statements is installed. Showing slow queries...';
    ELSE
        RAISE NOTICE 'pg_stat_statements is NOT installed. Run: CREATE EXTENSION pg_stat_statements;';
    END IF;
END $$;

-- Only run if extension exists (wrapped in DO block to prevent error)
DO $$
DECLARE
    query_rec RECORD;
BEGIN
    IF EXISTS (SELECT 1 FROM pg_extension WHERE extname = 'pg_stat_statements') THEN
        FOR query_rec IN
            SELECT
                calls,
                round(mean_exec_time::numeric, 2) as avg_time_ms,
                round(max_exec_time::numeric, 2) as max_time_ms,
                round(total_exec_time::numeric, 2) as total_time_ms,
                round((100 * total_exec_time / sum(total_exec_time) OVER ())::numeric, 2) as percent_total,
                left(query, 100) as query_preview
            FROM pg_stat_statements
            WHERE query NOT LIKE '%pg_stat_statements%'
              AND query NOT LIKE '%pg_catalog%'
            ORDER BY mean_exec_time DESC
            LIMIT 10
        LOOP
            RAISE NOTICE 'Calls: %, Avg: %ms, Max: %ms, Total: %ms, %%: %, Query: %',
                query_rec.calls,
                query_rec.avg_time_ms,
                query_rec.max_time_ms,
                query_rec.total_time_ms,
                query_rec.percent_total,
                query_rec.query_preview;
        END LOOP;
    END IF;
END $$;

\echo ''

-- ============================================================================
-- 11. Database Configuration
-- ============================================================================

\echo '11. KEY POSTGRESQL SETTINGS'
\echo '-----------------------------------------'

SELECT
    name,
    setting,
    unit,
    short_desc
FROM pg_settings
WHERE name IN (
    'max_connections',
    'shared_buffers',
    'effective_cache_size',
    'work_mem',
    'maintenance_work_mem',
    'random_page_cost',
    'effective_io_concurrency',
    'autovacuum',
    'checkpoint_completion_target',
    'wal_buffers',
    'default_statistics_target'
)
ORDER BY name;

\echo ''

-- ============================================================================
-- 12. Recommendations
-- ============================================================================

\echo '12. RECOMMENDATIONS'
\echo '-----------------------------------------'

DO $$
DECLARE
    conn_count int;
    hit_ratio numeric;
    max_conn int;
    bloat_tables int;
BEGIN
    -- Check connection count
    SELECT count(*) INTO conn_count
    FROM pg_stat_activity WHERE datname = 'jellyfin';

    SELECT setting::int INTO max_conn
    FROM pg_settings WHERE name = 'max_connections';

    IF conn_count::numeric / max_conn > 0.8 THEN
        RAISE WARNING 'High connection usage: % of % max connections', conn_count, max_conn;
        RAISE NOTICE 'RECOMMENDATION: Increase max_connections in postgresql.conf';
    END IF;

    -- Check cache hit ratio
    SELECT round(
        sum(blks_hit) / nullif(sum(blks_hit) + sum(blks_read), 0) * 100, 2
    ) INTO hit_ratio
    FROM pg_stat_database WHERE datname = 'jellyfin';

    IF hit_ratio < 95 THEN
        RAISE WARNING 'Low cache hit ratio: %. Target: >95%%', hit_ratio;
        RAISE NOTICE 'RECOMMENDATION: Increase shared_buffers and effective_cache_size';
    END IF;

    -- Check bloat
    SELECT count(*) INTO bloat_tables
    FROM pg_stat_user_tables
    WHERE n_dead_tup > 1000
      AND schemaname IN ('library', 'users', 'authentication');

    IF bloat_tables > 0 THEN
        RAISE WARNING 'Found % tables with significant dead tuples', bloat_tables;
        RAISE NOTICE 'RECOMMENDATION: Run VACUUM ANALYZE or adjust autovacuum settings';
    END IF;

    -- RAISE NOTICE 'Diagnostic report complete.';
END $$;

\echo ''
\echo '========================================='
\echo 'Run sql/performance_indexes.sql to add missing indexes'
\echo 'See PERFORMANCE_TROUBLESHOOTING.md for detailed fixes'
\echo '========================================='