-- Type: DO
-- Name: library_item
-- Description: Create library_item table

BEGIN;

CREATE EXTENSION IF NOT EXISTS vector;

CREATE TYPE library_item_state AS ENUM ('SUCCEEDED', 'FAILED', 'PROCESSING', 'ARCHIVED', 'DELETED');
CREATE TYPE content_reader_type AS ENUM ('WEB', 'PDF', 'EPUB');
CREATE TYPE directionality_type AS ENUM ('LTR', 'RTL');

CREATE TABLE omnivore.library_item (
    id uuid PRIMARY KEY DEFAULT uuid_generate_v1mc(),
    user_id uuid NOT NULL REFERENCES omnivore.user ON DELETE CASCADE,
    state library_item_state NOT NULL DEFAULT 'SUCCEEDED',
    original_url text NOT NULL,
    download_url text,
    slug text NOT NULL,
    title text NOT NULL,
    author text,
    description text,
    saved_at timestamptz NOT NULL DEFAULT current_timestamp,
    created_at timestamptz NOT NULL DEFAULT current_timestamp,
    published_at timestamptz,
    archived_at timestamptz,
    deleted_at timestamptz,
    read_at timestamptz,
    updated_at timestamptz NOT NULL DEFAULT current_timestamp,
    item_language text,
    word_count integer,
    site_name text,
    site_icon text,
    metadata JSON,
    reading_progress_last_read_anchor integer NOT NULL DEFAULT 0,
    reading_progress_highest_read_anchor integer NOT NULL DEFAULT 0,
    reading_progress_top_percent real NOT NULL DEFAULT 0,
    reading_progress_bottom_percent real NOT NULL DEFAULT 0,
    thumbnail text,
    item_type text NOT NULL DEFAULT 'UNKNOWN',
    upload_file_id uuid REFERENCES omnivore.upload_files ON DELETE CASCADE,
    content_reader content_reader_type NOT NULL DEFAULT 'WEB',
    original_content text,
    readable_content text NOT NULL DEFAULT '',
    content_tsv tsvector,
    site_tsv tsvector,
    title_tsv tsvector,
    author_tsv tsvector,
    description_tsv tsvector,
    search_tsv tsvector,
    model_name text,
    embedding vector(768),
    text_content_hash text,
    gcs_archive_id text,
    directionality directionality_type NOT NULL DEFAULT 'LTR',
    subscription text,
    label_names text[] NOT NULL DEFAULT array[]::text[], -- array of label names of the item
    highlight_labels text[] NOT NULL DEFAULT array[]::text[], -- array of label names of the item's highlights
    highlight_annotations text[] NOT NULL DEFAULT array[]::text[], -- array of highlight annotations of the item
    note text,
    note_tsv tsvector,
    UNIQUE (user_id, original_url)
);

CREATE TRIGGER update_library_item_modtime BEFORE UPDATE ON omnivore.library_item FOR EACH ROW EXECUTE PROCEDURE update_updated_at_column();

CREATE INDEX library_item_content_tsv_idx ON omnivore.library_item USING GIN (content_tsv);
CREATE INDEX library_item_site_tsv_idx ON omnivore.library_item USING GIN (site_tsv);
CREATE INDEX library_item_title_tsv_idx ON omnivore.library_item USING GIN (title_tsv);
CREATE INDEX library_item_author_tsv_idx ON omnivore.library_item USING GIN (author_tsv);
CREATE INDEX library_item_description_tsv_idx ON omnivore.library_item USING GIN (description_tsv);
CREATE INDEX library_item_search_tsv_idx ON omnivore.library_item USING GIN (search_tsv);
CREATE INDEX library_item_note_tsv_idx ON omnivore.library_item USING GIN (note_tsv);

CREATE OR REPLACE FUNCTION update_library_item_tsv() RETURNS trigger AS $$
begin
    new.content_tsv := to_tsvector('pg_catalog.english', coalesce(new.readable_content, ''));
    new.site_tsv := to_tsvector('pg_catalog.english', coalesce(new.site_name, ''));
    new.title_tsv := to_tsvector('pg_catalog.english', coalesce(new.title, ''));
    new.author_tsv := to_tsvector('pg_catalog.english', coalesce(new.author, ''));
    new.description_tsv := to_tsvector('pg_catalog.english', coalesce(new.description, ''));
    -- note_tsv is generated by both note and highlight_annotations
    new.note_tsv := to_tsvector('pg_catalog.english', coalesce(new.note, '') || ' ' || array_to_string(new.highlight_annotations, ' '));
    new.search_tsv := 
        setweight(new.title_tsv, 'A') || 
        setweight(new.author_tsv, 'A') || 
        setweight(new.site_tsv, 'A') || 
        setweight(new.description_tsv, 'A') || 
        -- full hostname (eg www.omnivore.app)
        setweight(to_tsvector('pg_catalog.english', coalesce(regexp_replace(new.original_url, '^((http[s]?):\/)?\/?([^:\/\s]+)((\/\w+)*\/)([\w\-\.]+[^#?\s]+)(.*)?(#[\w\-]+)?$', '\3'), '')), 'A') || 
        -- secondary hostname (eg omnivore)
        setweight(to_tsvector('pg_catalog.english', coalesce(regexp_replace(new.original_url, '^((http[s]?):\/)?\/?(.*\.)?([^:\/\s]+)(\..*)((\/+)*\/)?([\w\-\.]+[^#?\s]+)(.*)?(#[\w\-]+)?$', '\4'), '')), 'A') ||
        setweight(new.note_tsv, 'A') ||
        setweight(new.content_tsv, 'B');
    return new;
end
$$ LANGUAGE plpgsql;

CREATE TRIGGER library_item_tsv_update BEFORE INSERT OR UPDATE
    ON omnivore.library_item FOR EACH ROW EXECUTE PROCEDURE update_library_item_tsv();

ALTER TABLE omnivore.library_item ENABLE ROW LEVEL SECURITY;

CREATE POLICY library_item_policy ON omnivore.library_item 
    USING (user_id = omnivore.get_current_user_id())
    WITH CHECK (user_id = omnivore.get_current_user_id());

GRANT SELECT, INSERT, UPDATE, DELETE ON omnivore.library_item TO omnivore_user;

COMMIT;
