//! Database schema and migrations
//!
//! TRACES: UR-002 | DR-012 | IR-013

/// List of migrations to apply in order.
/// Each migration is a tuple of (name, sql).
pub const MIGRATIONS: &[(&str, &str)] = &[
    ("001_initial_schema", MIGRATION_001),
    ("002_remove_access_token", MIGRATION_002),
    ("003_relax_user_data_constraints", MIGRATION_003),
    ("004_enhance_downloads", MIGRATION_004),
    ("005_relax_downloads_fk", MIGRATION_005),
    ("006_downloads_metadata", MIGRATION_006),
    ("007_cache_metadata", MIGRATION_007),
    ("008_video_downloads", MIGRATION_008),
    ("009_people_tables", MIGRATION_009),
    ("010_playback_context", MIGRATION_010),
    ("011_user_player_settings", MIGRATION_011),
    ("012_download_source", MIGRATION_012),
    ("013_downloads_item_status_index", MIGRATION_013),
    ("014_series_audio_preferences", MIGRATION_014),
    ("015_device_id", MIGRATION_015),
    ("016_autoplay_max_episodes", MIGRATION_016),
    ("017_downloads_resume_url", MIGRATION_017),
    ("018_items_is_folder", MIGRATION_018),
    ("019_genres_cache", MIGRATION_019),
];

/// Initial schema migration
const MIGRATION_001: &str = r#"
-- Jellyfin servers the user has connected to
CREATE TABLE IF NOT EXISTS servers (
    id TEXT PRIMARY KEY,
    name TEXT NOT NULL,
    url TEXT NOT NULL UNIQUE,
    version TEXT,
    created_at TEXT DEFAULT CURRENT_TIMESTAMP,
    last_connected_at TEXT
);

-- User accounts on Jellyfin servers
CREATE TABLE IF NOT EXISTS users (
    id TEXT PRIMARY KEY,
    server_id TEXT NOT NULL REFERENCES servers(id) ON DELETE CASCADE,
    username TEXT NOT NULL,
    access_token TEXT,
    is_active INTEGER DEFAULT 0,
    created_at TEXT DEFAULT CURRENT_TIMESTAMP,
    last_login_at TEXT,
    UNIQUE(server_id, username)
);

-- Libraries/views from Jellyfin
CREATE TABLE IF NOT EXISTS libraries (
    id TEXT PRIMARY KEY,
    server_id TEXT NOT NULL REFERENCES servers(id) ON DELETE CASCADE,
    name TEXT NOT NULL,
    collection_type TEXT,
    image_tag TEXT,
    sort_order INTEGER DEFAULT 0,
    synced_at TEXT,
    UNIQUE(server_id, id)
);

-- Media items (movies, shows, episodes, albums, songs, artists)
CREATE TABLE IF NOT EXISTS items (
    id TEXT PRIMARY KEY,
    server_id TEXT NOT NULL REFERENCES servers(id) ON DELETE CASCADE,
    library_id TEXT REFERENCES libraries(id) ON DELETE SET NULL,
    parent_id TEXT REFERENCES items(id) ON DELETE CASCADE,

    -- Core metadata
    name TEXT NOT NULL,
    sort_name TEXT,
    original_title TEXT,
    item_type TEXT NOT NULL,  -- Movie, Series, Episode, MusicAlbum, Audio, MusicArtist, etc.

    -- Media info
    overview TEXT,
    tagline TEXT,
    genres TEXT,  -- JSON array
    tags TEXT,    -- JSON array
    studios TEXT, -- JSON array

    -- For episodes
    series_id TEXT,
    series_name TEXT,
    season_id TEXT,
    season_name TEXT,
    index_number INTEGER,        -- Episode number
    parent_index_number INTEGER, -- Season number

    -- For music
    album_id TEXT,
    album_name TEXT,
    album_artist TEXT,
    artists TEXT,  -- JSON array

    -- Dates
    premiere_date TEXT,
    production_year INTEGER,
    date_created TEXT,

    -- Runtime (ticks)
    runtime_ticks INTEGER,

    -- Images
    primary_image_tag TEXT,
    backdrop_image_tags TEXT,  -- JSON array

    -- Ratings
    community_rating REAL,
    official_rating TEXT,

    -- Sync metadata
    synced_at TEXT,
    etag TEXT,

    UNIQUE(server_id, id)
);

-- Full-text search index for items
CREATE VIRTUAL TABLE IF NOT EXISTS items_fts USING fts5(
    name,
    overview,
    album_name,
    album_artist,
    artists,
    series_name,
    content='items',
    content_rowid='rowid'
);

-- Triggers to keep FTS index in sync
CREATE TRIGGER IF NOT EXISTS items_ai AFTER INSERT ON items BEGIN
    INSERT INTO items_fts(rowid, name, overview, album_name, album_artist, artists, series_name)
    VALUES (new.rowid, new.name, new.overview, new.album_name, new.album_artist, new.artists, new.series_name);
END;

CREATE TRIGGER IF NOT EXISTS items_ad AFTER DELETE ON items BEGIN
    INSERT INTO items_fts(items_fts, rowid, name, overview, album_name, album_artist, artists, series_name)
    VALUES('delete', old.rowid, old.name, old.overview, old.album_name, old.album_artist, old.artists, old.series_name);
END;

CREATE TRIGGER IF NOT EXISTS items_au AFTER UPDATE ON items BEGIN
    INSERT INTO items_fts(items_fts, rowid, name, overview, album_name, album_artist, artists, series_name)
    VALUES('delete', old.rowid, old.name, old.overview, old.album_name, old.album_artist, old.artists, old.series_name);
    INSERT INTO items_fts(rowid, name, overview, album_name, album_artist, artists, series_name)
    VALUES (new.rowid, new.name, new.overview, new.album_name, new.album_artist, new.artists, new.series_name);
END;

-- Media streams (audio/subtitle tracks)
CREATE TABLE IF NOT EXISTS media_streams (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    item_id TEXT NOT NULL REFERENCES items(id) ON DELETE CASCADE,
    stream_index INTEGER NOT NULL,
    stream_type TEXT NOT NULL,  -- Audio, Subtitle, Video
    codec TEXT,
    language TEXT,
    display_title TEXT,
    is_default INTEGER DEFAULT 0,
    is_forced INTEGER DEFAULT 0,
    is_external INTEGER DEFAULT 0,
    path TEXT,  -- For external subtitles
    UNIQUE(item_id, stream_index)
);

-- User-specific data (watch progress, favorites)
CREATE TABLE IF NOT EXISTS user_data (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    item_id TEXT NOT NULL REFERENCES items(id) ON DELETE CASCADE,

    -- Playback state
    playback_position_ticks INTEGER DEFAULT 0,
    play_count INTEGER DEFAULT 0,
    is_played INTEGER DEFAULT 0,
    is_favorite INTEGER DEFAULT 0,

    -- Timestamps
    last_played_at TEXT,

    -- Sync status
    synced_at TEXT,
    pending_sync INTEGER DEFAULT 0,  -- 1 if local changes need sync

    UNIQUE(user_id, item_id)
);

-- Downloaded media files
CREATE TABLE IF NOT EXISTS downloads (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    item_id TEXT NOT NULL REFERENCES items(id) ON DELETE CASCADE,
    user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,

    -- File info
    file_path TEXT NOT NULL,
    file_size INTEGER,
    mime_type TEXT,

    -- Download state
    status TEXT DEFAULT 'pending',  -- pending, downloading, completed, failed, paused
    progress REAL DEFAULT 0,        -- 0.0 to 1.0

    -- Transcoding options used
    bitrate INTEGER,
    container TEXT,

    -- Timestamps
    queued_at TEXT DEFAULT CURRENT_TIMESTAMP,
    started_at TEXT,
    completed_at TEXT,

    -- Error tracking
    error_message TEXT,
    retry_count INTEGER DEFAULT 0,

    UNIQUE(item_id, user_id)
);

-- Offline mutation queue (changes to sync back to server)
CREATE TABLE IF NOT EXISTS sync_queue (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,

    -- Operation details
    operation TEXT NOT NULL,  -- mark_played, mark_favorite, update_progress, etc.
    item_id TEXT,
    payload TEXT,  -- JSON data for the operation

    -- Queue state
    status TEXT DEFAULT 'pending',  -- pending, processing, completed, failed
    retry_count INTEGER DEFAULT 0,

    -- Timestamps
    created_at TEXT DEFAULT CURRENT_TIMESTAMP,
    processed_at TEXT,

    -- Error tracking
    error_message TEXT
);

-- Cached thumbnails
CREATE TABLE IF NOT EXISTS thumbnails (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    item_id TEXT NOT NULL REFERENCES items(id) ON DELETE CASCADE,
    image_type TEXT NOT NULL,  -- Primary, Backdrop, Thumb, Logo, etc.
    image_tag TEXT NOT NULL,
    file_path TEXT NOT NULL,
    width INTEGER,
    height INTEGER,
    cached_at TEXT DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(item_id, image_type, image_tag)
);

-- User playlists (local + synced)
CREATE TABLE IF NOT EXISTS playlists (
    id TEXT PRIMARY KEY,
    user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    name TEXT NOT NULL,
    is_local INTEGER DEFAULT 0,  -- 1 for local-only playlists
    jellyfin_id TEXT,            -- NULL for local-only
    created_at TEXT DEFAULT CURRENT_TIMESTAMP,
    updated_at TEXT
);

-- Playlist items
CREATE TABLE IF NOT EXISTS playlist_items (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    playlist_id TEXT NOT NULL REFERENCES playlists(id) ON DELETE CASCADE,
    item_id TEXT NOT NULL REFERENCES items(id) ON DELETE CASCADE,
    sort_order INTEGER NOT NULL,
    added_at TEXT DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(playlist_id, item_id)
);

-- Indexes for common queries
CREATE INDEX IF NOT EXISTS idx_items_server ON items(server_id);
CREATE INDEX IF NOT EXISTS idx_items_library ON items(library_id);
CREATE INDEX IF NOT EXISTS idx_items_parent ON items(parent_id);
CREATE INDEX IF NOT EXISTS idx_items_type ON items(item_type);
CREATE INDEX IF NOT EXISTS idx_items_album ON items(album_id);
CREATE INDEX IF NOT EXISTS idx_items_series ON items(series_id);
CREATE INDEX IF NOT EXISTS idx_user_data_user ON user_data(user_id);
CREATE INDEX IF NOT EXISTS idx_user_data_item ON user_data(item_id);
CREATE INDEX IF NOT EXISTS idx_downloads_status ON downloads(status);
CREATE INDEX IF NOT EXISTS idx_sync_queue_status ON sync_queue(status);
CREATE INDEX IF NOT EXISTS idx_thumbnails_item ON thumbnails(item_id);
"#;

/// Migration to remove access_token column from users table
/// Tokens are now stored in the system keyring (or encrypted file fallback)
const MIGRATION_002: &str = r#"
-- Remove access_token column from users table
-- Tokens are now stored in secure storage (system keyring)

-- SQLite doesn't support DROP COLUMN in older versions, so we recreate the table
CREATE TABLE IF NOT EXISTS users_new (
    id TEXT PRIMARY KEY,
    server_id TEXT NOT NULL REFERENCES servers(id) ON DELETE CASCADE,
    username TEXT NOT NULL,
    is_active INTEGER DEFAULT 0,
    created_at TEXT DEFAULT CURRENT_TIMESTAMP,
    last_login_at TEXT,
    UNIQUE(server_id, username)
);

-- Copy existing data (excluding access_token)
INSERT OR IGNORE INTO users_new (id, server_id, username, is_active, created_at, last_login_at)
SELECT id, server_id, username, is_active, created_at, last_login_at FROM users;

-- Drop old table and rename new one
DROP TABLE IF EXISTS users;
ALTER TABLE users_new RENAME TO users;
"#;

/// Migration to relax foreign key constraints on user_data table
/// Allows tracking playback progress for items not yet synced to local database
const MIGRATION_003: &str = r#"
-- Recreate user_data table without foreign key constraint on item_id
-- This allows tracking playback progress for items that haven't been synced locally yet

CREATE TABLE IF NOT EXISTS user_data_new (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    item_id TEXT NOT NULL,  -- No foreign key constraint - item may not be synced yet

    -- Playback state
    playback_position_ticks INTEGER DEFAULT 0,
    play_count INTEGER DEFAULT 0,
    is_played INTEGER DEFAULT 0,
    is_favorite INTEGER DEFAULT 0,

    -- Timestamps
    last_played_at TEXT,

    -- Sync status
    synced_at TEXT,
    pending_sync INTEGER DEFAULT 0,  -- 1 if local changes need sync

    UNIQUE(user_id, item_id)
);

-- Copy existing data
INSERT OR IGNORE INTO user_data_new (id, user_id, item_id, playback_position_ticks, play_count, is_played, is_favorite, last_played_at, synced_at, pending_sync)
SELECT id, user_id, item_id, playback_position_ticks, play_count, is_played, is_favorite, last_played_at, synced_at, pending_sync FROM user_data;

-- Drop old table and rename new one
DROP TABLE IF EXISTS user_data;
ALTER TABLE user_data_new RENAME TO user_data;

-- Recreate index
CREATE INDEX IF NOT EXISTS idx_user_data_user ON user_data(user_id);
CREATE INDEX IF NOT EXISTS idx_user_data_item ON user_data(item_id);
"#;

/// Migration to enhance downloads table with priority and bytes_downloaded
const MIGRATION_004: &str = r#"
-- Add priority column for queue ordering
ALTER TABLE downloads ADD COLUMN priority INTEGER DEFAULT 0;

-- Add bytes_downloaded for resume support
ALTER TABLE downloads ADD COLUMN bytes_downloaded INTEGER DEFAULT 0;

-- Create index for efficient queue processing (priority DESC, FIFO within same priority)
CREATE INDEX IF NOT EXISTS idx_downloads_queue
  ON downloads(status, priority DESC, queued_at ASC)
  WHERE status IN ('pending', 'downloading');
"#;

/// Migration to relax foreign key constraint on downloads.item_id
/// Allows downloading items that haven't been synced to local database yet
const MIGRATION_005: &str = r#"
-- Recreate downloads table without foreign key constraint on item_id
-- This allows downloading items that haven't been synced locally yet

CREATE TABLE IF NOT EXISTS downloads_new (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    item_id TEXT NOT NULL,  -- No foreign key constraint - item may not be synced yet
    user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,

    -- File info
    file_path TEXT NOT NULL,
    file_size INTEGER,
    mime_type TEXT,

    -- Download state
    status TEXT DEFAULT 'pending',  -- pending, downloading, completed, failed, paused
    progress REAL DEFAULT 0,        -- 0.0 to 1.0

    -- Transcoding options used
    bitrate INTEGER,
    container TEXT,

    -- Timestamps
    queued_at TEXT DEFAULT CURRENT_TIMESTAMP,
    started_at TEXT,
    completed_at TEXT,

    -- Error tracking
    error_message TEXT,
    retry_count INTEGER DEFAULT 0,

    -- Priority and progress tracking (from migration 004)
    priority INTEGER DEFAULT 0,
    bytes_downloaded INTEGER DEFAULT 0,

    UNIQUE(item_id, user_id)
);

-- Copy existing data
INSERT OR IGNORE INTO downloads_new (
    id, item_id, user_id, file_path, file_size, mime_type, status, progress,
    bitrate, container, queued_at, started_at, completed_at, error_message,
    retry_count, priority, bytes_downloaded
)
SELECT
    id, item_id, user_id, file_path, file_size, mime_type, status, progress,
    bitrate, container, queued_at, started_at, completed_at, error_message,
    retry_count, priority, bytes_downloaded
FROM downloads;

-- Drop old table and rename new one
DROP TABLE IF EXISTS downloads;
ALTER TABLE downloads_new RENAME TO downloads;

-- Recreate indexes
CREATE INDEX IF NOT EXISTS idx_downloads_status ON downloads(status);
CREATE INDEX IF NOT EXISTS idx_downloads_queue
  ON downloads(status, priority DESC, queued_at ASC)
  WHERE status IN ('pending', 'downloading');
"#;

/// Migration to store item metadata directly in downloads table
/// This eliminates dependency on items table being synced and fixes UUID display issues
const MIGRATION_006: &str = r#"
-- Add columns to store item metadata directly in downloads
-- This ensures correct display even when items aren't synced locally
ALTER TABLE downloads ADD COLUMN item_name TEXT;
ALTER TABLE downloads ADD COLUMN artist_name TEXT;
ALTER TABLE downloads ADD COLUMN album_name TEXT;
"#;

/// Migration to enhance thumbnail caching with LRU eviction support
/// - Relaxes foreign key constraint on item_id (allows caching for items not yet synced)
/// - Adds last_accessed for LRU eviction
/// - Adds file_size for cache limit tracking
/// - Creates cache_settings table for configurable limits
const MIGRATION_007: &str = r#"
-- Recreate thumbnails table without foreign key constraint on item_id
-- and add LRU eviction support columns
CREATE TABLE IF NOT EXISTS thumbnails_new (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    item_id TEXT NOT NULL,  -- No foreign key constraint - item may not be synced yet
    image_type TEXT NOT NULL,  -- Primary, Backdrop, Thumb, Logo, etc.
    image_tag TEXT NOT NULL,
    file_path TEXT NOT NULL,
    width INTEGER,
    height INTEGER,
    file_size INTEGER DEFAULT 0,  -- Size in bytes for cache limit tracking
    cached_at TEXT DEFAULT CURRENT_TIMESTAMP,
    last_accessed TEXT DEFAULT CURRENT_TIMESTAMP,  -- For LRU eviction
    UNIQUE(item_id, image_type, image_tag)
);

-- Copy existing data (if any)
INSERT OR IGNORE INTO thumbnails_new (id, item_id, image_type, image_tag, file_path, width, height, cached_at)
SELECT id, item_id, image_type, image_tag, file_path, width, height, cached_at FROM thumbnails;

-- Drop old table and rename new one
DROP TABLE IF EXISTS thumbnails;
ALTER TABLE thumbnails_new RENAME TO thumbnails;

-- Create indexes for efficient queries
CREATE INDEX IF NOT EXISTS idx_thumbnails_item ON thumbnails(item_id);
CREATE INDEX IF NOT EXISTS idx_thumbnails_lru ON thumbnails(last_accessed ASC);

-- Cache settings table for configurable limits
CREATE TABLE IF NOT EXISTS cache_settings (
    key TEXT PRIMARY KEY,
    value TEXT NOT NULL,
    updated_at TEXT DEFAULT CURRENT_TIMESTAMP
);

-- Insert default settings
INSERT OR IGNORE INTO cache_settings (key, value) VALUES ('image_cache_limit_bytes', '1073741824');  -- 1GB default
INSERT OR IGNORE INTO cache_settings (key, value) VALUES ('image_cache_enabled', 'true');
"#;

/// Migration to add video download support and item pinning
/// - Adds video-specific metadata columns to downloads (series/episode info, quality preset)
/// - Adds media_type to distinguish audio vs video downloads
/// - Adds is_pinned column to items table for protecting metadata from cache clear
const MIGRATION_008: &str = r#"
-- Add video-specific metadata columns to downloads
ALTER TABLE downloads ADD COLUMN series_name TEXT;
ALTER TABLE downloads ADD COLUMN season_name TEXT;
ALTER TABLE downloads ADD COLUMN episode_number INTEGER;
ALTER TABLE downloads ADD COLUMN season_number INTEGER;
ALTER TABLE downloads ADD COLUMN quality_preset TEXT DEFAULT 'original';
ALTER TABLE downloads ADD COLUMN media_type TEXT DEFAULT 'audio';

-- Add pinning support to items table
-- Pinned items are protected from cache clear operations
ALTER TABLE items ADD COLUMN is_pinned INTEGER DEFAULT 0;

-- Index for efficiently finding pinned items
CREATE INDEX IF NOT EXISTS idx_items_pinned ON items(is_pinned) WHERE is_pinned = 1;

-- Index for efficiently querying downloads by series
CREATE INDEX IF NOT EXISTS idx_downloads_series ON downloads(series_name) WHERE series_name IS NOT NULL;

-- Index for filtering by media type
CREATE INDEX IF NOT EXISTS idx_downloads_media_type ON downloads(media_type);
"#;

/// Migration to add people/cast caching support
/// - Creates people table for caching actor/director/writer/etc info
/// - Creates item_people junction table for many-to-many relationships
/// - Adds indexes for efficient queries
const MIGRATION_009: &str = r#"
-- People table for caching cast/crew members
CREATE TABLE IF NOT EXISTS people (
    id TEXT PRIMARY KEY,
    server_id TEXT NOT NULL,
    name TEXT NOT NULL,
    overview TEXT,
    primary_image_tag TEXT,
    premiere_date TEXT,      -- Birth date
    end_date TEXT,           -- Death date
    synced_at TEXT DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(server_id, id)
);

-- Item-Person association table (many-to-many)
-- Stores which people appear in which items, along with role info
CREATE TABLE IF NOT EXISTS item_people (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    item_id TEXT NOT NULL,
    person_id TEXT NOT NULL,
    server_id TEXT NOT NULL,
    person_type TEXT NOT NULL,  -- Actor, Director, Writer, Producer, Composer, etc.
    role TEXT,                   -- Character name for actors
    sort_order INTEGER DEFAULT 0,
    synced_at TEXT DEFAULT CURRENT_TIMESTAMP,
    UNIQUE(item_id, person_id, person_type)
);

-- Indexes for efficient queries
CREATE INDEX IF NOT EXISTS idx_people_server ON people(server_id);
CREATE INDEX IF NOT EXISTS idx_people_name ON people(name);
CREATE INDEX IF NOT EXISTS idx_item_people_item ON item_people(item_id);
CREATE INDEX IF NOT EXISTS idx_item_people_person ON item_people(person_id);
CREATE INDEX IF NOT EXISTS idx_item_people_type ON item_people(person_type);
"#;

/// Migration to add playback context tracking
/// - Adds playback_context_type column to track if user played a container or single item
/// - Adds playback_context_id column to store the container ID (album/playlist)
/// - Adds index for efficient recently played queries
const MIGRATION_010: &str = r#"
-- Add playback context tracking to user_data
-- Tracks whether user played a container (album/playlist) or single item
ALTER TABLE user_data ADD COLUMN playback_context_type TEXT;
ALTER TABLE user_data ADD COLUMN playback_context_id TEXT;

-- Index for efficient recently played queries
CREATE INDEX IF NOT EXISTS idx_user_data_last_played
  ON user_data(user_id, last_played_at DESC)
  WHERE last_played_at IS NOT NULL;
"#;

/// Migration to add user-specific player settings
/// - Creates user_player_settings table for autoplay and audio preferences
/// - Note: Sleep timer state is NOT persisted (cancelled on app close)
/// - Autoplay settings control next episode behavior
/// - Audio settings for crossfade, gapless playback, and volume normalization
const MIGRATION_011: &str = r#"
-- User-specific player settings (autoplay and audio settings)
-- Sleep timer is NOT persisted here (maintained in-memory only)
CREATE TABLE IF NOT EXISTS user_player_settings (
    user_id TEXT PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,

    -- Autoplay settings
    autoplay_next_episode INTEGER DEFAULT 1,  -- 1 = enabled, 0 = disabled
    autoplay_countdown_seconds INTEGER DEFAULT 10,  -- 5-30 seconds

    -- Audio settings (crossfade, normalization)
    crossfade_duration REAL DEFAULT 0.0,  -- 0-12 seconds
    gapless_playback INTEGER DEFAULT 1,
    normalize_volume INTEGER DEFAULT 0,
    volume_level TEXT DEFAULT 'normal',  -- 'loud', 'normal', 'quiet'

    updated_at TEXT DEFAULT CURRENT_TIMESTAMP
);

-- Index for efficient user settings lookup
CREATE INDEX IF NOT EXISTS idx_user_player_settings_user ON user_player_settings(user_id);
"#;

/// Migration to track download source (user-initiated vs auto-cached)
/// - Adds download_source column to distinguish manual downloads from auto-caching
/// - Enables color-coded UI display
const MIGRATION_012: &str = r#"
-- Add download source tracking
-- Values: 'user' (explicit download), 'auto' (smart cache/queue precache)
ALTER TABLE downloads ADD COLUMN download_source TEXT DEFAULT 'user';

-- Index for filtering by source
CREATE INDEX IF NOT EXISTS idx_downloads_source ON downloads(download_source);
"#;

/// Migration to add composite index for offline mode filtering
/// - Adds index on (item_id, status) for efficient JOIN queries in OfflineRepository
/// - Significantly improves performance when filtering items by download status
const MIGRATION_013: &str = r#"
-- Add composite index for offline mode filtering
-- This speeds up queries that join items with downloads to show only downloaded content
CREATE INDEX IF NOT EXISTS idx_downloads_item_status ON downloads(item_id, status);
"#;

/// Migration to add series audio track preferences
/// - Stores user's preferred audio track per series
/// - Matches tracks by display title and language across episodes
/// - Falls back to default track if preferred track not found
const MIGRATION_014: &str = r#"
-- Series-specific audio track preferences
-- When user changes audio track for an episode, remember preference for the series
CREATE TABLE IF NOT EXISTS series_audio_preferences (
    user_id TEXT NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    series_id TEXT NOT NULL,
    server_id TEXT NOT NULL,

    -- Audio track info for matching across episodes
    audio_track_display_title TEXT,
    audio_track_language TEXT,
    audio_track_index INTEGER,

    updated_at TEXT DEFAULT CURRENT_TIMESTAMP,

    PRIMARY KEY (user_id, series_id, server_id)
);

-- Index for efficient lookups
CREATE INDEX IF NOT EXISTS idx_series_audio_prefs_user_series
  ON series_audio_preferences(user_id, series_id);
"#;

/// Migration to add device ID storage
/// - Creates app_settings table for app-wide configuration (device ID, etc.)
/// - Device ID is generated once and persisted for Jellyfin server identification
const MIGRATION_015: &str = r#"
-- App-wide settings table for device ID and other app-level configuration
-- Device ID is a unique identifier for this app installation
-- Required for Jellyfin server communication and session tracking
CREATE TABLE IF NOT EXISTS app_settings (
    key TEXT PRIMARY KEY,
    value TEXT NOT NULL,
    updated_at TEXT DEFAULT CURRENT_TIMESTAMP
);

-- Create index for efficient lookups (though key is already primary key)
CREATE INDEX IF NOT EXISTS idx_app_settings_key ON app_settings(key);
"#;

/// Migration to add autoplay episode limit setting
/// - Adds autoplay_max_episodes column to user_player_settings
/// - 0 = unlimited (default), any positive value limits consecutive auto-plays
const MIGRATION_016: &str = r#"
ALTER TABLE user_player_settings
ADD COLUMN autoplay_max_episodes INTEGER DEFAULT 0;
"#;

/// Migration to persist the resolved stream URL and target directory on each
/// download row. This lets the backend queue pump start a pending download by
/// itself (replaying the stored URL) once a concurrency slot frees up, instead
/// of relying on the frontend to re-issue every queued item.
const MIGRATION_017: &str = r#"
-- Resolved download source URL and on-disk target directory, captured when the
-- download is enqueued. Nullable: pre-existing rows and rows enqueued without a
-- URL simply won't be auto-started by the pump.
ALTER TABLE downloads ADD COLUMN stream_url TEXT;
ALTER TABLE downloads ADD COLUMN target_dir TEXT;
"#;

/// Migration to record whether a cached item is a folder/container vs a playable
/// leaf. Needed so channel items (which can be either) route to the player or to
/// a browse list correctly. Existing cached rows predate the column and have an
/// unknown folder flag, so we force a refresh by clearing their `synced_at`,
/// causing the hybrid repository to re-fetch them from the server on next browse.
const MIGRATION_018: &str = r#"
ALTER TABLE items ADD COLUMN is_folder INTEGER DEFAULT 0;

-- Force re-fetch of all cached items so is_folder is populated from the server.
UPDATE items SET synced_at = NULL;
"#;

/// Migration to cache the full server genre catalog. Previously genres were
/// derived on the fly from cached albums, which meant offline (and the hybrid
/// cache-first race) only ever saw genres for the handful of locally-cached
/// albums — collapsing the variety on the music/TV/movie landing pages. This
/// table stores the complete genre list per library so offline has the real
/// catalog and cache-first routing returns the right thing.
const MIGRATION_019: &str = r#"
CREATE TABLE IF NOT EXISTS genres (
    id TEXT NOT NULL,
    server_id TEXT NOT NULL,
    library_id TEXT,
    name TEXT NOT NULL,
    album_count INTEGER,
    synced_at TEXT DEFAULT CURRENT_TIMESTAMP,
    PRIMARY KEY (server_id, library_id, name)
);

CREATE INDEX IF NOT EXISTS idx_genres_scope ON genres(server_id, library_id);
"#;
