-- name: CreateUser :one
INSERT INTO users (email, username, password_hash, first_name, last_name, theme, role)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING id, email, username, theme, first_name, last_name, role, created_at, updated_at;

-- name: GetUserByEmail :one
SELECT id, email, username, theme, first_name, last_name, role, created_at, updated_at FROM users WHERE email = $1;

-- name: GetUserByUsername :one
SELECT id, email, username, theme, first_name, last_name, role, created_at, updated_at FROM users WHERE username = $1;

-- name: GetUserByEmailOrUsername :one
SELECT id, email, username, theme, first_name, last_name, role, created_at, updated_at FROM users WHERE email = $1 OR username = $1;

-- name: GetUserForLogin :one
SELECT id, email, username, password_hash, theme, first_name, last_name, role, created_at, updated_at FROM users WHERE email = $1 OR username = $1;

-- name: GetUser :one
SELECT id, email, username, theme, first_name, last_name, role, created_at, updated_at FROM users WHERE id = $1;

-- name: GetUserPasswordHash :one
SELECT password_hash FROM users WHERE id = $1;

-- name: ListUsers :many
SELECT id, email, username, theme, first_name, last_name, role, created_at, updated_at FROM users ORDER BY created_at DESC;

-- Library Types queries
-- name: GetLibraryTypes :many
SELECT * FROM library_types ORDER BY name;

-- name: GetLibraryType :one
SELECT * FROM library_types WHERE id = $1;

-- name: GetLibraryTypeByName :one
SELECT * FROM library_types WHERE name = $1;

-- Libraries queries
-- name: CreateLibrary :one
INSERT INTO libraries (name, description, library_type_id, created_by_admin_id)
VALUES ($1, $2, $3, $4)
RETURNING *;

-- name: GetLibrary :one
SELECT l.*, lt.name as type_name, lt.description as type_description
FROM libraries l
JOIN library_types lt ON l.library_type_id = lt.id
WHERE l.id = $1;

-- name: ListLibraries :many
SELECT l.*, lt.name as type_name, lt.description as type_description
FROM libraries l
JOIN library_types lt ON l.library_type_id = lt.id
ORDER BY l.created_at DESC;

-- name: UpdateLibrary :one
UPDATE libraries SET
    name = $2,
    description = $3,
    updated_at = NOW()
WHERE id = $1
RETURNING *;

-- name: DeleteLibrary :exec
DELETE FROM libraries WHERE id = $1;

-- Library Folders queries
-- name: AddLibraryFolder :one
INSERT INTO library_folders (library_id, folder_path) VALUES ($1, $2) RETURNING *;

-- name: GetLibraryFolders :many
SELECT * FROM library_folders WHERE library_id = $1 ORDER BY created_at;

-- name: DeleteLibraryFolder :one
DELETE FROM library_folders WHERE library_id = $1 AND folder_path = $2 RETURNING *;

-- name: GetLibraryByFolder :one
SELECT lf.library_id, l.* FROM library_folders lf
JOIN libraries l ON lf.library_id = l.id
WHERE lf.folder_path = $1;

-- Library Visibility queries
-- name: SetLibraryVisibility :one
INSERT INTO library_visibility (user_id, library_id, is_visible)
VALUES ($1, $2, $3)
ON CONFLICT (user_id, library_id)
DO UPDATE SET
    is_visible = EXCLUDED.is_visible,
    updated_at = NOW()
RETURNING *;

-- name: GetLibraryVisibility :one
SELECT * FROM library_visibility WHERE user_id = $1 AND library_id = $2;

-- name: GetUserVisibleLibraries :many
SELECT l.*, lt.name as type_name, lt.description as type_description,
       COALESCE(lv.is_visible, true) as is_visible
FROM libraries l
JOIN library_types lt ON l.library_type_id = lt.id
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = $1
WHERE COALESCE(lv.is_visible, true) = true
ORDER BY l.created_at DESC;

-- Media Items queries
-- name: CreateMediaItem :one
INSERT INTO media_items (library_id, title, author, isbn, description, file_path, file_size, mime_type, cover_image_path, series, series_number, tags, asin, date_published, publisher, contributors, language, edition, page_count, genre, copyright_year, goodreads_id, openlibrary_id, google_books_id, added_by_admin_id)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20, $21, $22, $23, $24, $25)
RETURNING *;

-- name: GetMediaItem :one
SELECT * FROM media_items WHERE id = $1;

-- name: ListMediaItems :many
SELECT mi.*, l.name as library_name, lt.name as library_type_name
FROM media_items mi
JOIN libraries l ON mi.library_id = l.id
JOIN library_types lt ON l.library_type_id = lt.id
ORDER BY mi.created_at DESC LIMIT $1 OFFSET $2;

-- name: ListMediaItemsByLibrary :many
SELECT mi.*, l.name as library_name, lt.name as library_type_name
FROM media_items mi
JOIN libraries l ON mi.library_id = l.id
JOIN library_types lt ON l.library_type_id = lt.id
WHERE mi.library_id = $1
ORDER BY mi.created_at DESC;

-- name: ListMediaItemsSorted :many
SELECT mi.*, l.name as library_name, lt.name as library_type_name
FROM media_items mi
JOIN libraries l ON mi.library_id = l.id
JOIN library_types lt ON l.library_type_id = lt.id
WHERE mi.library_id = sqlc.narg('library_id')
ORDER BY
    CASE
        WHEN sqlc.narg('sort') = 'title ASC' THEN mi.title
        ELSE ''
    END ASC,
    CASE
        WHEN sqlc.narg('sort') = 'title DESC' THEN mi.title
        ELSE ''
    END DESC,
    CASE
        WHEN sqlc.narg('sort') = 'author ASC' THEN COALESCE(mi.author, '')
        ELSE ''
    END ASC,
    CASE
        WHEN sqlc.narg('sort') = 'author DESC' THEN COALESCE(mi.author, '')
        ELSE ''
    END DESC,
    CASE
        WHEN sqlc.narg('sort') = 'created_at ASC' THEN mi.created_at
        ELSE '1970-01-01'::timestamp
    END ASC,
    CASE
        WHEN sqlc.narg('sort') = 'created_at DESC' THEN mi.created_at
        ELSE '1970-01-01'::timestamp
    END DESC,
    CASE
        WHEN sqlc.narg('sort') = 'series ASC' THEN COALESCE(mi.series, '')
        ELSE ''
    END ASC,
    CASE
        WHEN sqlc.narg('sort') = 'series DESC' THEN COALESCE(mi.series, '')
        ELSE ''
    END DESC,
    CASE
        WHEN sqlc.narg('sort') = 'date_published ASC' THEN COALESCE(mi.date_published::text, '')
        ELSE ''
    END ASC,
    CASE
        WHEN sqlc.narg('sort') = 'date_published DESC' THEN COALESCE(mi.date_published::text, '')
        ELSE ''
    END DESC,
    CASE
        WHEN sqlc.narg('sort') = 'copyright_year ASC' THEN COALESCE(mi.copyright_year::text, '0')
        ELSE ''
    END ASC,
    CASE
        WHEN sqlc.narg('sort') = 'copyright_year DESC' THEN COALESCE(mi.copyright_year::text, '0')
        ELSE ''
    END DESC,
    CASE
        WHEN sqlc.narg('sort') = 'page_count ASC' THEN COALESCE(mi.page_count::text, '0')
        ELSE ''
    END ASC,
    CASE
        WHEN sqlc.narg('sort') = 'page_count DESC' THEN COALESCE(mi.page_count::text, '0')
        ELSE ''
    END DESC,
    CASE
        WHEN sqlc.narg('sort') = 'genre ASC' THEN COALESCE(mi.genre, '')
        ELSE ''
    END ASC,
    CASE
        WHEN sqlc.narg('sort') = 'genre DESC' THEN COALESCE(mi.genre, '')
        ELSE ''
    END DESC,
    mi.created_at DESC
 LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');

-- name: ListMediaItemsFiltered :many
SELECT mi.*, l.name as library_name, lt.name as library_type_name
FROM media_items mi
JOIN libraries l ON mi.library_id = l.id
JOIN library_types lt ON l.library_type_id = lt.id
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
WHERE mi.library_id = sqlc.narg('library_id')
  AND COALESCE(lv.is_visible, true) = true
  AND (sqlc.narg('author_filter') = '' OR mi.author ILIKE sqlc.narg('author_filter'))
  AND (sqlc.narg('series_filter') = '' OR mi.series ILIKE sqlc.narg('series_filter'))
  AND (sqlc.narg('genre_filter') = '' OR mi.genre = sqlc.narg('genre_filter'))
  AND (sqlc.narg('language_filter') = '' OR mi.language = sqlc.narg('language_filter'))
  AND (sqlc.narg('year_min') = 0 OR mi.copyright_year >= sqlc.narg('year_min'))
  AND (sqlc.narg('year_max') = 0 OR mi.copyright_year <= sqlc.narg('year_max'))
  AND (sqlc.narg('has_cover') = false OR mi.cover_image_path IS NOT NULL)
ORDER BY
    CASE
        WHEN sqlc.narg('sort') = 'title ASC' THEN mi.title
        ELSE ''
    END ASC,
    CASE
        WHEN sqlc.narg('sort') = 'title DESC' THEN mi.title
        ELSE ''
    END DESC,
    CASE
        WHEN sqlc.narg('sort') = 'author ASC' THEN COALESCE(mi.author, '')
        ELSE ''
    END ASC,
    CASE
        WHEN sqlc.narg('sort') = 'author DESC' THEN COALESCE(mi.author, '')
        ELSE ''
    END DESC,
    CASE
        WHEN sqlc.narg('sort') = 'created_at ASC' THEN mi.created_at
        ELSE '1970-01-01'::timestamp
    END ASC,
    CASE
        WHEN sqlc.narg('sort') = 'created_at DESC' THEN mi.created_at
        ELSE '1970-01-01'::timestamp
    END DESC,
    mi.created_at DESC
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');


-- name: UpdateMediaItem :one
UPDATE media_items SET
    title = $2,
    author = $3,
    isbn = $4,
    description = $5,
    cover_image_path = $6,
    series = $7,
    series_number = $8,
    tags = $9,
    asin = $10,
    date_published = $11,
    publisher = $12,
    contributors = $13,
    language = $14,
    edition = $15,
    page_count = $16,
    genre = $17,
    copyright_year = $18,
    goodreads_id = $19,
    openlibrary_id = $20,
    google_books_id = $21,
    updated_at = NOW()
WHERE id = $1
RETURNING *;

-- name: DeleteMediaItem :exec
DELETE FROM media_items WHERE id = $1;

-- name: GetMediaItemByFilePath :one
SELECT * FROM media_items WHERE file_path = $1;

-- name: GetReadingProgress :one
SELECT * FROM reading_progress WHERE media_item_id = $1 AND user_id = $2;

-- name: UpdateReadingProgress :one
INSERT INTO reading_progress (media_item_id, user_id, current_page, total_pages, last_read_at)
VALUES ($1, $2, $3, $4, NOW())
ON CONFLICT (media_item_id, user_id)
DO UPDATE SET
    current_page = EXCLUDED.current_page,
    total_pages = EXCLUDED.total_pages,
    last_read_at = NOW()
RETURNING *;

-- name: DeleteReadingProgress :exec
DELETE FROM reading_progress WHERE media_item_id = $1 AND user_id = $2;

-- name: UpdateUserTheme :exec
UPDATE users SET theme = $2, updated_at = NOW() WHERE id = $1;

-- name: UpdateUserProfile :exec
UPDATE users SET first_name = $2, last_name = $3, updated_at = NOW() WHERE id = $1;

-- name: UpdateUsername :exec
UPDATE users SET username = $2, updated_at = NOW() WHERE id = $1;

-- name: UpdateEmail :exec
UPDATE users SET email = $2, updated_at = NOW() WHERE id = $1;

-- name: UpdatePassword :[REDACTED]
UPDATE users SET password_hash = $2, updated_at = NOW() WHERE id = $1;

-- name: DeleteUser :exec
DELETE FROM users WHERE id = $1;

-- name: UpdateScanSettings :exec
UPDATE users SET scan_frequency_minutes = $2, auto_scan_enabled = $3, updated_at = NOW() WHERE id = $1;

-- name: GetScanSettings :one
SELECT scan_frequency_minutes, auto_scan_enabled FROM users WHERE id = $1;

-- name: CreateMediaRating :one
INSERT INTO media_ratings (media_item_id, user_id, rating)
VALUES ($1, $2, $3)
ON CONFLICT (media_item_id, user_id)
DO UPDATE SET
    rating = EXCLUDED.rating,
    updated_at = NOW()
RETURNING *;

-- name: GetMediaRating :one
SELECT * FROM media_ratings WHERE media_item_id = $1 AND user_id = $2;

-- name: GetMediaRatings :many
SELECT mr.*, u.username
FROM media_ratings mr
JOIN users u ON mr.user_id = u.id
WHERE mr.media_item_id = $1
ORDER BY mr.created_at DESC;

-- name: UpdateMediaRating :one
UPDATE media_ratings SET
    rating = $3,
    updated_at = NOW()
WHERE media_item_id = $1 AND user_id = $2
RETURNING *;

-- name: DeleteMediaRating :exec
DELETE FROM media_ratings WHERE media_item_id = $1 AND user_id = $2;

-- Note: User ebook folders replaced by library folders system
-- Legacy folder management is now handled through libraries

-- Search Media Items queries
-- name: SearchMediaItems :many
SELECT mi.*, l.name as library_name, lt.name as library_type_name
FROM media_items mi
JOIN libraries l ON mi.library_id = l.id
JOIN library_types lt ON l.library_type_id = lt.id
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
WHERE COALESCE(lv.is_visible, true) = true
  AND (
      mi.title ILIKE sqlc.narg('search_pattern') OR 
      mi.author ILIKE sqlc.narg('search_pattern') OR 
      mi.series ILIKE sqlc.narg('search_pattern') OR
      mi.tags ILIKE sqlc.narg('search_pattern') OR
      mi.contributors ILIKE sqlc.narg('search_pattern')
  )
ORDER BY 
  CASE 
      WHEN mi.title ILIKE sqlc.narg('search_pattern') THEN 1
      WHEN mi.author ILIKE sqlc.narg('search_pattern') THEN 2
      WHEN mi.series ILIKE sqlc.narg('search_pattern') THEN 3
      WHEN mi.tags ILIKE sqlc.narg('search_pattern') THEN 4
      ELSE 5
  END,
  mi.title ASC
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');

-- name: SearchMediaItemsFuzzy :many
SELECT mi.*, l.name as library_name, lt.name as library_type_name
FROM media_items mi
JOIN libraries l ON mi.library_id = l.id
JOIN library_types lt ON l.library_type_id = lt.id
LEFT JOIN library_visibility lv ON l.id = lv.library_id AND lv.user_id = sqlc.narg('user_id')
WHERE COALESCE(lv.is_visible, true) = true
  AND (
      word_similarity(sqlc.narg('search_query'), mi.title) > 0.3 OR
      word_similarity(sqlc.narg('search_query'), COALESCE(mi.author, '')) > 0.3 OR
      word_similarity(sqlc.narg('search_query'), COALESCE(mi.series, '')) > 0.3 OR
      word_similarity(sqlc.narg('search_query'), COALESCE(mi.tags, '')) > 0.3 OR
      word_similarity(sqlc.narg('search_query'), COALESCE(mi.contributors, '')) > 0.3
  )
ORDER BY 
  GREATEST(
      word_similarity(sqlc.narg('search_query'), mi.title),
      word_similarity(sqlc.narg('search_query'), COALESCE(mi.author, '')),
      word_similarity(sqlc.narg('search_query'), COALESCE(mi.series, '')),
      word_similarity(sqlc.narg('search_query'), COALESCE(mi.tags, '')),
      word_similarity(sqlc.narg('search_query'), COALESCE(mi.contributors, ''))
  ) DESC,
  mi.title ASC
LIMIT sqlc.narg('limit') OFFSET sqlc.narg('offset');

-- Media Notes queries
-- name: CreateMediaNote :one
INSERT INTO media_notes (media_item_id, user_id, content, position)
VALUES ($1, $2, $3, $4)
RETURNING *;

-- name: GetMediaNote :one
SELECT * FROM media_notes WHERE id = $1;

-- name: GetMediaNotes :many
SELECT * FROM media_notes WHERE media_item_id = $1 AND user_id = $2 ORDER BY created_at DESC;

-- name: UpdateMediaNote :one
UPDATE media_notes SET
    content = $2,
    position = $3,
    updated_at = NOW()
WHERE id = $1
RETURNING *;

-- name: DeleteMediaNote :exec
DELETE FROM media_notes WHERE id = $1;

-- Media Highlights queries
-- name: CreateMediaHighlight :one
INSERT INTO media_highlights (media_item_id, user_id, selection_text, start_position, end_position, color, note_id)
VALUES ($1, $2, $3, $4, $5, $6, $7)
RETURNING *;

-- name: GetMediaHighlight :one
SELECT * FROM media_highlights WHERE id = $1;

-- name: GetMediaHighlights :many
SELECT * FROM media_highlights WHERE media_item_id = $1 AND user_id = $2 ORDER BY created_at DESC;

-- name: UpdateMediaHighlight :one
UPDATE media_highlights SET
    selection_text = $2,
    start_position = $3,
    end_position = $4,
    color = $5,
    note_id = $6,
    updated_at = NOW()
WHERE id = $1
RETURNING *;

-- name: DeleteMediaHighlight :exec
DELETE FROM media_highlights WHERE id = $1;

-- Backward compatibility - Ebook Notes queries (using views)
-- name: CreateEbookNote :one
INSERT INTO media_notes (media_item_id, user_id, content, position)
VALUES ($1, $2, $3, $4)
RETURNING *;

-- name: UpdateEbookNote :one
UPDATE media_notes SET
    content = $2,
    position = $3,
    updated_at = NOW()
WHERE id = $1
RETURNING *;

-- name: DeleteEbookNote :exec
DELETE FROM media_notes WHERE id = $1;

-- Refresh Tokens queries
-- name: CreateRefreshToken :one
INSERT INTO refresh_tokens (user_id, token, expires_at)
VALUES ($1, $2, $3)
RETURNING *;

-- name: GetRefreshToken :one
SELECT rt.*, u.email, u.username, u.role
FROM refresh_tokens rt
JOIN users u ON rt.user_id = u.id
WHERE rt.token = $1 AND rt.revoked_at IS NULL AND rt.expires_at > NOW();

-- name: RevokeRefreshToken :exec
UPDATE refresh_tokens SET revoked_at = NOW() WHERE token = $1;

-- name: RevokeAllUserRefreshTokens :exec
UPDATE refresh_tokens SET revoked_at = NOW() WHERE user_id = $1 AND revoked_at IS NULL;

-- name: CleanupExpiredRefreshTokens :exec
DELETE FROM refresh_tokens WHERE expires_at < NOW() OR (revoked_at IS NOT NULL AND revoked_at < NOW() - INTERVAL '7 days');

-- ============================================
-- PHASE 1: FORMAT DETECTION & PROGRESS (Week 2)
-- ============================================

-- Update media item format group information
-- name: UpdateMediaItemFormatGroup :exec
UPDATE media_items
SET
    format_group = $2,
    format_mimetype = $3,
    is_reflowable = $4,
    has_fixed_layout = $5,
    total_characters = $6,
    chapter_count = $7,
    updated_at = NOW()
WHERE id = $1;

-- Bulk update format group for all media items
-- name: BulkUpdateFormatGroups :exec
UPDATE media_items m
SET
    format_group = detect_format_group(m.mime_type, m.file_path),
    format_mimetype = m.mime_type,
    is_reflowable = (detect_format_group(m.mime_type, m.file_path) = 'reflowable'),
    has_fixed_layout = (detect_format_group(m.mime_type, m.file_path) IN ('fixed_layout', 'comic_archive')),
    updated_at = NOW()
WHERE m.format_group IS NULL OR m.format_group = 'unknown';

-- Get universal progress for a book
-- name: GetUniversalProgress :one
SELECT
    rp.id,
    rp.media_item_id,
    rp.user_id,
    rp.current_page,
    rp.total_pages,
    rp.last_read_at,
    rp.percentage,
    rp.character_offset,
    rp.epubcfi,
    rp.chapter,
    rp.chapter_progress,
    rp.viewport_x,
    rp.viewport_y,
    rp.zoom_level,
    rp.scroll_position_x,
    rp.scroll_position_y,
    rp.panel_number,
    rp.reading_mode,
    rp.last_sync_device,
    rp.last_sync_source,
    rp.last_sync_timestamp,
    rp.conflict_detected,
    rp.conflict_resolved,
    mi.format_group,
    mi.format_mimetype,
    mi.is_reflowable,
    mi.has_fixed_layout,
    mi.total_characters,
    mi.chapter_count
FROM reading_progress rp
JOIN media_items mi ON rp.media_item_id = mi.id
WHERE rp.media_item_id = $1 AND rp.user_id = $2;

-- Update universal progress
-- name: UpdateUniversalProgress :one
INSERT INTO reading_progress (
    media_item_id,
    user_id,
    percentage,
    character_offset,
    epubcfi,
    chapter,
    chapter_progress,
    viewport_x,
    viewport_y,
    zoom_level,
    scroll_position_x,
    scroll_position_y,
    panel_number,
    reading_mode,
    last_sync_device,
    last_sync_source,
    last_sync_timestamp,
    current_page,
    total_pages,
    last_read_at
)
VALUES (
    $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, NOW(), $17, $18, NOW()
)
ON CONFLICT (media_item_id, user_id)
DO UPDATE SET
    percentage = EXCLUDED.percentage,
    character_offset = EXCLUDED.character_offset,
    epubcfi = EXCLUDED.epubcfi,
    chapter = EXCLUDED.chapter,
    chapter_progress = EXCLUDED.chapter_progress,
    viewport_x = EXCLUDED.viewport_x,
    viewport_y = EXCLUDED.viewport_y,
    zoom_level = EXCLUDED.zoom_level,
    scroll_position_x = EXCLUDED.scroll_position_x,
    scroll_position_y = EXCLUDED.scroll_position_y,
    panel_number = EXCLUDED.panel_number,
    reading_mode = EXCLUDED.reading_mode,
    last_sync_device = EXCLUDED.last_sync_device,
    last_sync_source = EXCLUDED.last_sync_source,
    last_sync_timestamp = EXCLUDED.last_sync_timestamp,
    current_page = EXCLUDED.current_page,
    total_pages = EXCLUDED.total_pages,
    last_read_at = NOW()
RETURNING *;

-- Get reading history for a user and book
-- name: GetReadingHistory :many
SELECT *
FROM reading_history
WHERE user_id = $1 AND media_item_id = $2
ORDER BY created_at DESC
LIMIT $3;

-- Create reading history entry
-- name: CreateReadingHistory :one
INSERT INTO reading_history (
    user_id,
    media_item_id,
    device_id,
    progress_percentage,
    reading_session_start,
    reading_session_end,
    pages_read,
    time_spent_seconds,
    device_metadata
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
RETURNING *;

-- Media Items Admin Operations