-- Consolidated Bookmann Database Schema
-- This file contains the complete current schema for the Bookmann media library management system

-- Create library types table
CREATE TABLE library_types (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(50) UNIQUE NOT NULL,
    description TEXT,
    allowed_extensions TEXT[] NOT NULL, -- Array of allowed file extensions for this type
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Insert default library types
INSERT INTO library_types (name, description, allowed_extensions) VALUES
('ebooks', 'Ebook files including EPUB, PDF, MOBI, etc.', ARRAY['.epub', '.pdf', '.mobi', '.azw', '.azw3', '.txt', '.rtf', '.doc', '.docx', '.lit', '.fb2', '.pdb']),
('comics', 'Comic book archives and image formats', ARRAY['.cbz', '.cbr', '.cb7', '.cbt', '.pdf']),
('manga', 'Manga files including archives and image folders', ARRAY['.cbz', '.cbr', '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp']);

-- Create users table
CREATE TABLE users (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    email VARCHAR(255) UNIQUE NOT NULL,
    username VARCHAR(255) UNIQUE NOT NULL,
    password_hash VARCHAR(255) NOT NULL,
    first_name VARCHAR(255),
    last_name VARCHAR(255),
    role VARCHAR(20) NOT NULL DEFAULT 'user' CHECK (role IN ('admin', 'user')),
    theme VARCHAR(50) DEFAULT 'tokyo-night',
    scan_frequency_minutes INTEGER DEFAULT 60,
    auto_scan_enabled BOOLEAN DEFAULT true,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Create refresh_tokens table
CREATE TABLE refresh_tokens (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    token VARCHAR(255) UNIQUE NOT NULL,
    expires_at TIMESTAMP WITH TIME ZONE NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    revoked_at TIMESTAMP WITH TIME ZONE
);

-- Create libraries table
CREATE TABLE libraries (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(255) NOT NULL,
    description TEXT,
    library_type_id UUID NOT NULL REFERENCES library_types(id) ON DELETE RESTRICT,
    created_by_admin_id UUID REFERENCES users(id) ON DELETE SET NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Create library_folders table for multiple folders per library
CREATE TABLE library_folders (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    library_id UUID NOT NULL REFERENCES libraries(id) ON DELETE CASCADE,
    folder_path VARCHAR(500) NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    UNIQUE(library_id, folder_path)
);

-- Create library_visibility table for user-specific library visibility
CREATE TABLE library_visibility (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    library_id UUID NOT NULL REFERENCES libraries(id) ON DELETE CASCADE,
    is_visible BOOLEAN NOT NULL DEFAULT true,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    UNIQUE(user_id, library_id)
);

-- Create media_items table (replaces ebooks table for broader media support)
CREATE TABLE media_items (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    library_id UUID NOT NULL REFERENCES libraries(id) ON DELETE CASCADE,
    title VARCHAR(255) NOT NULL,
    author VARCHAR(255),
    isbn VARCHAR(13), -- Still relevant for ebooks
    description TEXT,
    file_path VARCHAR(500) NOT NULL,
    file_size BIGINT,
    mime_type VARCHAR(100),
    cover_image_path VARCHAR(500),
    series VARCHAR(255),
    series_number INTEGER,
    tags TEXT,
    asin VARCHAR(20), -- Still relevant for ebooks
    date_published DATE,
    publisher VARCHAR(255),
    contributors TEXT,
    -- Enhanced fields for better metadata and functionality
    language VARCHAR(10) DEFAULT 'en', -- Language code (ISO 639-1)
    edition VARCHAR(100), -- Edition information
    page_count INTEGER, -- Total page count
    genre VARCHAR(100), -- Genre classification
    copyright_year INTEGER, -- Copyright/publication year
    goodreads_id VARCHAR(50), -- Goodreads identifier
    openlibrary_id VARCHAR(50), -- Open Library identifier
    google_books_id VARCHAR(100), -- Google Books identifier
    added_by_admin_id UUID REFERENCES users(id) ON DELETE SET NULL,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    -- Universal Sync Format Detection (Phase 1)
    format_group VARCHAR(20) NOT NULL DEFAULT 'reflowable',
    format_mimetype VARCHAR(100),
    is_reflowable BOOLEAN DEFAULT TRUE,
    has_fixed_layout BOOLEAN DEFAULT FALSE,
    total_characters BIGINT,
    chapter_count INTEGER,
    -- Kobo-Specific Metadata (Phase 4)
    entitlement_id VARCHAR(255) UNIQUE,
    revision_number INTEGER DEFAULT 1,
    kobo_content_id VARCHAR(255),
    kobo_metadata JSONB
);

-- Create ebooks view for backward compatibility
CREATE VIEW ebooks AS
SELECT mi.* 
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 lt.name = 'ebooks';

-- Create reading_progress table
CREATE TABLE reading_progress (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    media_item_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    current_page INTEGER DEFAULT 0,
    total_pages INTEGER,
    last_read_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    UNIQUE(media_item_id, user_id),
    -- Universal Progress Tracking (Phase 1)
    percentage FLOAT CHECK (percentage >= 0 AND percentage <= 1),
    character_offset BIGINT,
    epubcfi TEXT,
    chapter INTEGER,
    chapter_progress FLOAT CHECK (chapter_progress >= 0 AND chapter_progress <= 1),
    viewport_x FLOAT DEFAULT 0,
    viewport_y FLOAT DEFAULT 0,
    zoom_level FLOAT DEFAULT 1.0,
    scroll_position_x FLOAT DEFAULT 0,
    scroll_position_y FLOAT DEFAULT 0,
    panel_number INTEGER,
    reading_mode VARCHAR(20),
    -- Device Sync Metadata (Phase 1)
    last_sync_device VARCHAR(50),
    last_sync_source VARCHAR(20),
    last_sync_timestamp TIMESTAMP WITH TIME ZONE,
    conflict_detected BOOLEAN DEFAULT FALSE,
    conflict_resolved BOOLEAN DEFAULT TRUE
);

-- Create reading_progress view for backward compatibility
CREATE VIEW ebook_reading_progress AS
SELECT rp.*,
       mi.id as ebook_id -- Map media_item_id to ebook_id for compatibility
FROM reading_progress rp
JOIN media_items mi ON rp.media_item_id = mi.id
JOIN libraries l ON mi.library_id = l.id
JOIN library_types lt ON l.library_type_id = lt.id
WHERE lt.name = 'ebooks';

-- Create media_ratings table (replaces ebook_ratings)
CREATE TABLE media_ratings (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    media_item_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    rating INTEGER NOT NULL CHECK (rating >= 1 AND rating <= 10), -- 10-point scale for half-star precision
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    UNIQUE(media_item_id, user_id)
);

-- Create media_notes table for user notes on media items
CREATE TABLE media_notes (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    media_item_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    content TEXT NOT NULL,
    position VARCHAR(100), -- optional position (page:offset or CFI) for standalone notes
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    -- Location Enhancements (Phase 1)
    percentage_location FLOAT,
    character_start INTEGER,
    character_end INTEGER,
    epubcfi_location TEXT,
    chapter_reference INTEGER,
    paragraph_reference INTEGER,
    device_sync_data JSONB
);

-- Create media_highlights table for user highlights on media items
CREATE TABLE media_highlights (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    media_item_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    selection_text TEXT NOT NULL,
    start_position VARCHAR(100), -- position (page:offset or CFI) where highlight starts
    end_position VARCHAR(100),   -- position (page:offset or CFI) where highlight ends
    color VARCHAR(7) DEFAULT '#ffff00', -- hex color code for highlight
    note_id UUID REFERENCES media_notes(id) ON DELETE SET NULL, -- optional associated note
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    -- Location Enhancements (Phase 1)
    percentage_start FLOAT,
    percentage_end FLOAT,
    character_start INTEGER,
    character_end INTEGER,
    epubcfi_start TEXT,
    epubcfi_end TEXT,
    chapter_reference INTEGER,
    paragraph_start INTEGER,
    paragraph_end INTEGER,
    panel_number INTEGER,
    device_sync_data JSONB
);

-- ============================================
-- DEVICE REGISTRY (Phase 1)
-- ============================================
CREATE TABLE devices (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    device_name VARCHAR(100) NOT NULL,
    device_type VARCHAR(20) NOT NULL,
    device_identifier VARCHAR(255) UNIQUE NOT NULL,
    auth_token VARCHAR(500) UNIQUE NOT NULL,
    last_sync TIMESTAMP WITH TIME ZONE,
    last_seen TIMESTAMP WITH TIME ZONE,
    sync_enabled BOOLEAN DEFAULT TRUE,
    auto_sync BOOLEAN DEFAULT TRUE,
    sync_frequency_minutes INTEGER DEFAULT 5,
    device_metadata JSONB,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- ============================================
-- SYNC QUEUE FOR OFFLINE SUPPORT (Phase 1)
-- ============================================
CREATE TABLE sync_queue (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    device_id UUID NOT NULL REFERENCES devices(id) ON DELETE CASCADE,
    media_item_id UUID REFERENCES media_items(id) ON DELETE CASCADE,
    sync_type VARCHAR(20) NOT NULL,
    sync_data JSONB NOT NULL,
    priority INTEGER DEFAULT 5,
    attempts INTEGER DEFAULT 0,
    max_attempts INTEGER DEFAULT 3,
    status VARCHAR(20) DEFAULT 'pending',
    error_message TEXT,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    processed_at TIMESTAMP WITH TIME ZONE
);

-- ============================================
-- CONFLICT RESOLUTION (Phase 1)
-- ============================================
CREATE TABLE sync_conflicts (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    media_item_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    conflict_type VARCHAR(20) NOT NULL,
    conflict_data JSONB NOT NULL,
    resolution_status VARCHAR(20) DEFAULT 'unresolved',
    resolution_data JSONB,
    resolved_by UUID REFERENCES users(id),
    resolved_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- ============================================
-- KOBO SHELF MANAGEMENT (Phase 4)
-- ============================================
CREATE TABLE kobo_shelves (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    device_id UUID NOT NULL REFERENCES devices(id) ON DELETE CASCADE,
    media_item_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
    shelf_name VARCHAR(100) DEFAULT 'Default',
    shelf_position INTEGER DEFAULT 0,
    added_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    last_synced_at TIMESTAMP WITH TIME ZONE,
    UNIQUE(device_id, media_item_id)
);

CREATE INDEX idx_kobo_shelves_device_id ON kobo_shelves(device_id);
CREATE INDEX idx_kobo_shelves_media_item_id ON kobo_shelves(media_item_id);
CREATE INDEX idx_kobo_shelves_shelf_name ON kobo_shelves(shelf_name);

-- ============================================
-- KOBO ENTITLEMENTS TRACKING (Phase 4)
-- ============================================
CREATE TABLE kobo_entitlements (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    device_id UUID NOT NULL REFERENCES devices(id) ON DELETE CASCADE,
    media_item_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
    entitlement_id VARCHAR(255) NOT NULL,
    content_id VARCHAR(255) NOT NULL,
    revision_number INTEGER DEFAULT 1,
    purchase_date TIMESTAMP WITH TIME ZONE,
    accession_date TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    book_status VARCHAR(20) DEFAULT 'installed',
    sync_status VARCHAR(20) DEFAULT 'synced',
    kobo_metadata JSONB,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    UNIQUE(device_id, entitlement_id)
);

CREATE INDEX idx_kobo_entitlements_device_id ON kobo_entitlements(device_id);
CREATE INDEX idx_kobo_entitlements_media_item_id ON kobo_entitlements(media_item_id);
CREATE INDEX idx_kobo_entitlements_entitlement_id ON kobo_entitlements(entitlement_id);
CREATE INDEX idx_kobo_entitlements_content_id ON kobo_entitlements(content_id);

-- ============================================
-- READING HISTORY (Phase 1)
-- ============================================
CREATE TABLE reading_history (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
    media_item_id UUID NOT NULL REFERENCES media_items(id) ON DELETE CASCADE,
    device_id UUID REFERENCES devices(id),
    progress_percentage FLOAT,
    reading_session_start TIMESTAMP WITH TIME ZONE,
    reading_session_end TIMESTAMP WITH TIME ZONE,
    pages_read INTEGER,
    time_spent_seconds INTEGER,
    device_metadata JSONB,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

-- Create ebook_ratings view for backward compatibility
CREATE VIEW ebook_ratings AS
SELECT mr.*,
       mi.id as ebook_id -- Map media_item_id to ebook_id for compatibility
FROM media_ratings mr
JOIN media_items mi ON mr.media_item_id = mi.id
JOIN libraries l ON mi.library_id = l.id
JOIN library_types lt ON l.library_type_id = lt.id
WHERE lt.name = 'ebooks';

-- Create ebook_notes view for backward compatibility
CREATE VIEW ebook_notes AS
SELECT mn.*,
       mi.id as ebook_id -- Map media_item_id to ebook_id for compatibility
FROM media_notes mn
JOIN media_items mi ON mn.media_item_id = mi.id
JOIN libraries l ON mi.library_id = l.id
JOIN library_types lt ON l.library_type_id = lt.id
WHERE lt.name = 'ebooks';

-- Create ebook_highlights view for backward compatibility
CREATE VIEW ebook_highlights AS
SELECT mh.*,
       mi.id as ebook_id -- Map media_item_id to ebook_id for compatibility
FROM media_highlights mh
JOIN media_items mi ON mh.media_item_id = mi.id
JOIN libraries l ON mi.library_id = l.id
JOIN library_types lt ON l.library_type_id = lt.id
WHERE lt.name = 'ebooks';

-- Note: user_ebook_folders table is replaced by library_folders table
-- Libraries now handle folder management instead of individual users

-- Create indexes for better query performance
CREATE INDEX idx_users_email ON users(email);
CREATE INDEX idx_users_username ON users(username);
CREATE INDEX idx_library_types_name ON library_types(name);
CREATE INDEX idx_refresh_tokens_token ON refresh_tokens(token);
CREATE INDEX idx_refresh_tokens_user_id ON refresh_tokens(user_id);

-- Library indexes
CREATE INDEX idx_libraries_name ON libraries(name);
CREATE INDEX idx_libraries_library_type_id ON libraries(library_type_id);
CREATE INDEX idx_libraries_created_by_admin_id ON libraries(created_by_admin_id);
CREATE INDEX idx_library_folders_library_id ON library_folders(library_id);
CREATE INDEX idx_library_visibility_user_id ON library_visibility(user_id);
CREATE INDEX idx_library_visibility_library_id ON library_visibility(library_id);

-- Media items indexes
CREATE INDEX idx_media_items_title ON media_items(title);
CREATE INDEX idx_media_items_author ON media_items(author);
CREATE INDEX idx_media_items_library_id ON media_items(library_id);
CREATE INDEX idx_media_items_added_by_admin_id ON media_items(added_by_admin_id);
CREATE INDEX idx_media_items_genre ON media_items(genre);
CREATE INDEX idx_media_items_language ON media_items(language);
CREATE INDEX idx_media_items_copyright_year ON media_items(copyright_year);
CREATE INDEX idx_media_items_page_count ON media_items(page_count);
CREATE INDEX idx_media_items_series ON media_items(series);

-- Progress and ratings indexes
CREATE INDEX idx_reading_progress_media_item_id ON reading_progress(media_item_id);
CREATE INDEX idx_reading_progress_user_id ON reading_progress(user_id);
CREATE INDEX idx_media_ratings_media_item_id ON media_ratings(media_item_id);
CREATE INDEX idx_media_ratings_user_id ON media_ratings(user_id);

-- Notes and highlights indexes
CREATE INDEX idx_media_notes_media_item_id ON media_notes(media_item_id);
CREATE INDEX idx_media_notes_user_id ON media_notes(user_id);
CREATE INDEX idx_media_highlights_media_item_id ON media_highlights(media_item_id);
CREATE INDEX idx_media_highlights_user_id ON media_highlights(user_id);
CREATE INDEX idx_media_highlights_note_id ON media_highlights(note_id);

-- ============================================
-- NEW TABLE INDEXES (Phase 1)
-- ============================================

-- Device registry indexes
CREATE INDEX idx_devices_user_id ON devices(user_id);
CREATE INDEX idx_devices_device_type ON devices(device_type);
CREATE INDEX idx_devices_device_identifier ON devices(device_identifier);

-- Sync queue indexes
CREATE INDEX idx_sync_queue_device_id ON sync_queue(device_id);
CREATE INDEX idx_sync_queue_status ON sync_queue(status);
CREATE INDEX idx_sync_queue_priority ON sync_queue(priority);

-- Sync conflicts indexes
CREATE INDEX idx_sync_conflicts_media_item_id ON sync_conflicts(media_item_id);
CREATE INDEX idx_sync_conflicts_user_id ON sync_conflicts(user_id);
CREATE INDEX idx_sync_conflicts_status ON sync_conflicts(resolution_status);

-- Reading history indexes
CREATE INDEX idx_reading_history_user_id ON reading_history(user_id);
CREATE INDEX idx_reading_history_media_item_id ON reading_history(media_item_id);
CREATE INDEX idx_reading_history_created_at ON reading_history(created_at DESC);

-- ============================================
-- TRIGGER FUNCTIONS (Phase 1)
-- ============================================

-- Update updated_at timestamp for devices
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
  NEW.updated_at = NOW();
  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Trigger for devices table
CREATE TRIGGER update_devices_updated_at
  BEFORE UPDATE ON devices
  FOR EACH ROW
  EXECUTE FUNCTION update_updated_at_column();

-- Add comment explaining the rating system
COMMENT ON COLUMN media_ratings.rating IS 'Rating scale 1-10 (odd numbers = half-stars: 1,3,5,7,9 = 0.5,1.5,2.5,3.5,4.5 stars)';

-- Enhanced metadata comments
COMMENT ON COLUMN media_items.language IS 'ISO 639-1 language code (e.g., en, es, fr)';
COMMENT ON COLUMN media_items.copyright_year IS 'Copyright or publication year for filtering/sorting';
COMMENT ON COLUMN media_items.page_count IS 'Total page count for progress tracking and sorting';
COMMENT ON COLUMN media_items.genre IS 'Genre classification for filtering and discovery';
COMMENT ON COLUMN media_items.edition IS 'Edition information (e.g., "First Edition", "Revised Edition")';
COMMENT ON COLUMN media_items.goodreads_id IS 'Goodreads book identifier for integration';
COMMENT ON COLUMN media_items.openlibrary_id IS 'Open Library identifier for integration';
COMMENT ON COLUMN media_items.google_books_id IS 'Google Books identifier for integration';

-- Library Type File Extensions Notes:
-- - Ebooks: .epub, .pdf, .mobi, .azw, .azw3, .txt, .rtf, .doc, .docx, .lit, .fb2, .pdb
-- - Comics: .cbz, .cbr, .cb7, .cbt, .pdf
-- - Manga: .cbz, .cbr, .png, .jpg, .jpeg, .gif, .bmp, .webp (note: manga includes image folders)

-- Role System Notes:
-- - All users default to 'user' role
-- - Admin users can: create/manage libraries and folders, scan media, modify media metadata, delete media, control library visibility
-- - Regular users can: view visible libraries, rate media, track reading progress, create notes and highlights, manage their profile
-- - To create first admin: UPDATE users SET role = 'admin' WHERE email = 'your-admin-email';
-- - Library visibility is controlled through library_visibility table - admins can hide/show libraries per user
-- - Notes and highlights support position data (page:offset or CFI format) for precise location tracking
-- - Highlights can have associated notes for detailed annotations
-- - Backward compatibility views (ebooks, ebook_ratings, ebook_reading_progress, ebook_notes, ebook_highlights) maintain existing API contracts

-- ============================================
-- SYNC HELPER FUNCTIONS (Phase 1)
-- ============================================

-- Detect format group based on mimetype and file path
CREATE OR REPLACE FUNCTION detect_format_group(p_mimetype VARCHAR, p_file_path VARCHAR)
RETURNS VARCHAR AS $$
BEGIN
  CASE
    -- Reflowable formats
    WHEN p_mimetype = 'application/epub+zip' THEN
      RETURN 'reflowable';
    WHEN p_mimetype = 'application/x-mobipocket-ebook' THEN
      RETURN 'reflowable';
    WHEN p_mimetype = 'application/vnd.amazon.mobi8-ebook' THEN
      RETURN 'reflowable';
    WHEN p_file_path LIKE '%.epub' THEN
      RETURN 'reflowable';
    WHEN p_file_path LIKE '%.mobi' THEN
      RETURN 'reflowable';
    WHEN p_file_path LIKE '%.azw3' THEN
      RETURN 'reflowable';
    WHEN p_file_path LIKE '%.fb2' THEN
      RETURN 'reflowable';
    WHEN p_file_path LIKE '%.txt' THEN
      RETURN 'reflowable';

    -- Fixed layout formats
    WHEN p_mimetype = 'application/pdf' THEN
      RETURN 'fixed_layout';
    WHEN p_file_path LIKE '%.pdf' THEN
      RETURN 'fixed_layout';
    WHEN p_file_path LIKE '%.djvu' THEN
      RETURN 'fixed_layout';

    -- Comic archive formats
    WHEN p_mimetype = 'application/x-cbr' THEN
      RETURN 'comic_archive';
    WHEN p_mimetype = 'application/x-cbz' THEN
      RETURN 'comic_archive';
    WHEN p_file_path LIKE '%.cbz' THEN
      RETURN 'comic_archive';
    WHEN p_file_path LIKE '%.cbr' THEN
      RETURN 'comic_archive';
    WHEN p_file_path LIKE '%.cbt' THEN
      RETURN 'comic_archive';
    WHEN p_file_path LIKE '%.cb7' THEN
      RETURN 'comic_archive';

    ELSE
      RETURN 'unknown';
    END CASE;
END;
$$ LANGUAGE plpgsql;

-- Convert progress between format groups
CREATE OR REPLACE FUNCTION convert_progress(
  source_format VARCHAR,
  target_format VARCHAR,
  source_progress JSONB
) RETURNS JSONB AS $$
DECLARE
  result JSONB;
  percentage FLOAT;
BEGIN
  -- Extract percentage from source
  CASE source_format
    WHEN 'reflowable' THEN
      percentage := (source_progress->>'percentage')::FLOAT;
    WHEN 'fixed_layout' THEN
      percentage := ((source_progress->>'page')::FLOAT /
                     (source_progress->>'total_pages')::FLOAT);
    WHEN 'comic_archive' THEN
      percentage := ((source_progress->>'page')::FLOAT /
                     (source_progress->>'total_pages')::FLOAT);
    ELSE
      percentage := 0.0;
  END CASE;

  -- Build target format progress
  CASE target_format
    WHEN 'reflowable' THEN
      result := jsonb_build_object(
        'percentage', percentage,
        'epubcfi', 'epubcfi(/6/4/2:' || CAST(percentage * 100 AS INTEGER) || ')'
      );
    WHEN 'fixed_layout' THEN
      result := jsonb_build_object(
        'page', CAST(percentage * 200 AS INTEGER),
        'total_pages', 200,
        'percentage', percentage
      );
    WHEN 'comic_archive' THEN
      result := jsonb_build_object(
        'page', CAST(percentage * 32 AS INTEGER),
        'total_pages', 32,
        'percentage', percentage
      );
    ELSE
      result := '{}'::jsonb;
  END CASE;

  RETURN result;
END;
$$ LANGUAGE plpgsql;

-- Detect conflicts in sync progress
CREATE OR REPLACE FUNCTION detect_conflict(
  p_media_item_id UUID,
  new_progress JSONB,
  device_type VARCHAR
) RETURNS BOOLEAN AS $$
DECLARE
  existing_progress JSONB;
  time_diff INTERVAL;
BEGIN
  -- Get most recent progress from different device
  SELECT jsonb_build_object(
    'percentage', percentage,
    'timestamp', last_sync_timestamp
  ) INTO existing_progress
  FROM reading_progress
  WHERE media_item_id = p_media_item_id
    AND last_sync_source != device_type
  ORDER BY last_sync_timestamp DESC
  LIMIT 1;

  -- If no existing progress, no conflict
  IF existing_progress IS NULL THEN
    RETURN FALSE;
  END IF;

  -- Check time difference (within 5 minutes = potential conflict)
  time_diff := NOW() - (existing_progress->>'timestamp')::TIMESTAMP WITH TIME ZONE;

  IF time_diff < INTERVAL '5 minutes' THEN
    -- Check if progress significantly different
    IF ABS((new_progress->>'percentage')::FLOAT -
           (existing_progress->>'percentage')::FLOAT) > 0.01 THEN
      RETURN TRUE;
    END IF;
  END IF;

  RETURN FALSE;
END;
$$ LANGUAGE plpgsql;

-- Merge progress from two sources
CREATE OR REPLACE FUNCTION merge_progress(
  progress_a JSONB,
  progress_b JSONB
) RETURNS JSONB AS $$
BEGIN
  RETURN jsonb_build_object(
    'percentage', GREATEST(
      (progress_a->>'percentage')::FLOAT,
      (progress_b->>'percentage')::FLOAT
    ),
    'merged_from', ARRAY[
      (progress_a->>'source'),
      (progress_b->>'source')
    ],
    'merge_timestamp', NOW()
  );
END;
$$ LANGUAGE plpgsql;

-- ============================================
-- KOREADER SYNC FUNCTIONS (Phase 3)
-- ============================================

-- Bulk update progress from KOReader sync data
CREATE OR REPLACE FUNCTION bulk_update_progress_from_koreader(
  p_user_id UUID,
  p_sync_data JSONB
) RETURNS TABLE (
  media_item_id UUID,
  success BOOLEAN,
  message TEXT
) AS $$
DECLARE
  book_record JSONB;
  media_uuid UUID;
  existing_progress reading_progress%ROWTYPE;
  conflict_detected BOOLEAN;
BEGIN
  FOR book_record IN SELECT * FROM jsonb_array_elements(p_sync_data->'books')
  LOOP
    -- Try to find media item by UUID
    media_uuid := NULL;
    SELECT id INTO media_uuid FROM media_items WHERE id = (book_record->>'uuid')::uuid;

    -- If not found by UUID, try to match by file path
    IF media_uuid IS NULL AND book_record ? 'file_path' THEN
      SELECT id INTO media_uuid FROM media_items WHERE file_path = book_record->>'file_path';
    END IF;

    -- If still not found, try to match by title + author
    IF media_uuid IS NULL THEN
      SELECT id INTO media_uuid FROM media_items 
      WHERE title = book_record->>'title' 
        AND author = book_record->>'author'
      LIMIT 1;
    END IF;

    -- If no match found, return failure
    IF media_uuid IS NULL THEN
      RETURN QUERY SELECT NULL::uuid, FALSE, 'media item not found';
      CONTINUE;
    END IF;

    -- Check for conflicts
    conflict_detected := detect_conflict(media_uuid, book_record, 'koreader');

    -- Check if progress exists
    SELECT * INTO existing_progress FROM reading_progress 
    WHERE media_item_id = media_uuid AND user_id = p_user_id;

    -- Update or insert progress
    IF existing_progress.media_item_id IS NOT NULL THEN
      UPDATE reading_progress SET
        percentage = (book_record->>'percentage')::FLOAT,
        character_offset = CASE WHEN book_record ? 'character' THEN (book_record->>'character')::BIGINT ELSE existing_progress.character_offset END,
        epubcfi = CASE WHEN book_record ? 'epubcfi' THEN (book_record->>'epubcfi')::TEXT ELSE existing_progress.epubcfi END,
        chapter = CASE WHEN book_record ? 'chapter' THEN (book_record->>'chapter')::INTEGER ELSE existing_progress.chapter END,
        chapter_progress = (book_record->>'percentage')::FLOAT,
        last_sync_device = 'koreader',
        last_sync_source = 'koreader',
        last_sync_timestamp = NOW(),
        conflict_detected = conflict_detected,
        conflict_resolved = NOT conflict_detected,
        last_read_at = CASE WHEN book_record ? 'last_read' THEN (book_record->>'last_read')::TIMESTAMP WITH TIME ZONE ELSE NOW() END,
        current_page = CASE WHEN book_record ? 'page' THEN (book_record->>'page')::INTEGER ELSE existing_progress.current_page END,
        total_pages = CASE WHEN book_record ? 'total_pages' THEN (book_record->>'total_pages')::INTEGER ELSE existing_progress.total_pages END
      WHERE media_item_id = media_uuid AND user_id = p_user_id;
    ELSE
      INSERT INTO reading_progress (
        media_item_id,
        user_id,
        percentage,
        character_offset,
        epubcfi,
        chapter,
        chapter_progress,
        last_sync_device,
        last_sync_source,
        last_sync_timestamp,
        conflict_detected,
        conflict_resolved,
        last_read_at,
        current_page,
        total_pages
      ) VALUES (
        media_uuid,
        p_user_id,
        (book_record->>'percentage')::FLOAT,
        CASE WHEN book_record ? 'character' THEN (book_record->>'character')::BIGINT ELSE NULL END,
        CASE WHEN book_record ? 'epubcfi' THEN (book_record->>'epubcfi')::TEXT ELSE NULL END,
        CASE WHEN book_record ? 'chapter' THEN (book_record->>'chapter')::INTEGER ELSE NULL END,
        (book_record->>'percentage')::FLOAT,
        'koreader',
        'koreader',
        NOW(),
        conflict_detected,
        NOT conflict_detected,
        CASE WHEN book_record ? 'last_read' THEN (book_record->>'last_read')::TIMESTAMP WITH TIME ZONE ELSE NOW() END,
        CASE WHEN book_record ? 'page' THEN (book_record->>'page')::INTEGER ELSE NULL END,
        CASE WHEN book_record ? 'total_pages' THEN (book_record->>'total_pages')::INTEGER ELSE NULL END
      );
    END IF;

    -- Create conflict record if detected
    IF conflict_detected THEN
      INSERT INTO sync_conflicts (media_item_id, user_id, conflict_type, conflict_data)
      VALUES (media_uuid, p_user_id, 'progress', jsonb_build_object(
        'koreader', book_record,
        'timestamp', NOW()
      ));
    END IF;

    RETURN QUERY SELECT media_uuid, TRUE, 
      CASE WHEN conflict_detected THEN 'conflict detected' ELSE 'success' END;
  END LOOP;
END;
$$ LANGUAGE plpgsql;
