CREATE TABLE IF NOT EXISTS sources (
    id            INTEGER PRIMARY KEY,
    kind          TEXT    NOT NULL CHECK (kind IN ('jmap','imap','caldav','carddav','webdav','managesieve','maildir','exchange_ews','exchange_graph','takeout')),
    session_url   TEXT    NOT NULL,
    account_id    TEXT    NOT NULL,
    account_name  TEXT,
    username      TEXT    NOT NULL,
    UNIQUE (kind, session_url, account_id)
);

CREATE TABLE IF NOT EXISTS blobs (
    id    INTEGER PRIMARY KEY,
    hash  BLOB    NOT NULL UNIQUE,
    data  BLOB    NOT NULL
);

CREATE INDEX IF NOT EXISTS blobs_hash_idx ON blobs (hash);

CREATE TABLE IF NOT EXISTS sync_id_jmap (
    source_id   INTEGER NOT NULL REFERENCES sources(id) ON DELETE CASCADE,
    type_name   TEXT    NOT NULL,
    jmap_id     TEXT    NOT NULL,
    local_id    INTEGER NOT NULL,
    PRIMARY KEY (source_id, type_name, jmap_id),
    UNIQUE (source_id, type_name, local_id)
);

CREATE INDEX IF NOT EXISTS sync_id_jmap_local_idx
    ON sync_id_jmap (type_name, local_id);

CREATE TABLE IF NOT EXISTS sync_state_jmap (
    source_id   INTEGER NOT NULL REFERENCES sources(id) ON DELETE CASCADE,
    type_name   TEXT    NOT NULL,
    state       TEXT    NOT NULL,
    PRIMARY KEY (source_id, type_name)
);

CREATE TABLE IF NOT EXISTS sync_id_imap (
    source_id    INTEGER NOT NULL REFERENCES sources(id) ON DELETE CASCADE,
    type_name    TEXT    NOT NULL CHECK (type_name IN ('mailbox','email')),
    folder       TEXT    NOT NULL,
    uidvalidity  INTEGER NOT NULL,
    uid          INTEGER NOT NULL,
    local_id     INTEGER NOT NULL,
    PRIMARY KEY (source_id, type_name, folder, uidvalidity, uid),
    UNIQUE (source_id, type_name, local_id)
);

CREATE INDEX IF NOT EXISTS sync_id_imap_folder_idx
    ON sync_id_imap (source_id, type_name, folder);

CREATE TABLE IF NOT EXISTS imap_folder_state (
    source_id    INTEGER NOT NULL REFERENCES sources(id) ON DELETE CASCADE,
    folder       TEXT    NOT NULL,
    uidvalidity  INTEGER NOT NULL,
    uidnext      INTEGER NOT NULL,
    last_seen    TEXT    NOT NULL,
    PRIMARY KEY (source_id, folder)
);

CREATE TABLE IF NOT EXISTS sync_id_dav (
    source_id        INTEGER NOT NULL REFERENCES sources(id) ON DELETE CASCADE,
    type_name        TEXT    NOT NULL CHECK (type_name IN (
                                                 'calendar','calendarevent',
                                                 'addressbook','contactcard',
                                                 'filenode')),
    collection_href  TEXT    NOT NULL,
    item_href        TEXT    NOT NULL,
    etag             TEXT    NOT NULL DEFAULT '',
    local_id         INTEGER NOT NULL,
    PRIMARY KEY (source_id, type_name, item_href),
    UNIQUE (source_id, type_name, local_id)
);

CREATE INDEX IF NOT EXISTS sync_id_dav_collection_idx
    ON sync_id_dav (source_id, type_name, collection_href);

CREATE TABLE IF NOT EXISTS sync_id_managesieve (
    source_id  INTEGER NOT NULL REFERENCES sources(id) ON DELETE CASCADE,
    name       TEXT    NOT NULL,
    local_id   INTEGER NOT NULL,
    PRIMARY KEY (source_id, name),
    UNIQUE (source_id, local_id)
);

CREATE TABLE IF NOT EXISTS sync_id_maildir (
    source_id   INTEGER NOT NULL REFERENCES sources(id) ON DELETE CASCADE,
    type_name   TEXT    NOT NULL CHECK (type_name IN ('mailbox','email')),
    folder      TEXT    NOT NULL,
    unique_id   TEXT    NOT NULL,
    local_id    INTEGER NOT NULL,
    PRIMARY KEY (source_id, type_name, folder, unique_id),
    UNIQUE (source_id, type_name, local_id)
);

CREATE INDEX IF NOT EXISTS sync_id_maildir_folder_idx
    ON sync_id_maildir (source_id, type_name, folder);

CREATE TABLE IF NOT EXISTS sync_id_exchange_ews (
    source_id   INTEGER NOT NULL REFERENCES sources(id) ON DELETE CASCADE,
    type_name   TEXT    NOT NULL CHECK (type_name IN (
                                            'mailbox','calendar','addressbook',
                                            'email','calendarevent','contactcard')),
    folder_id   TEXT    NOT NULL DEFAULT '',
    item_id     TEXT    NOT NULL,
    change_key  TEXT    NOT NULL DEFAULT '',
    sync_state  TEXT    NOT NULL DEFAULT '',
    local_id    INTEGER NOT NULL,
    PRIMARY KEY (source_id, type_name, item_id),
    UNIQUE (source_id, type_name, local_id)
);

CREATE INDEX IF NOT EXISTS sync_id_exchange_ews_folder_idx
    ON sync_id_exchange_ews (source_id, type_name, folder_id);

CREATE TABLE IF NOT EXISTS sync_id_exchange_graph (
    source_id   INTEGER NOT NULL REFERENCES sources(id) ON DELETE CASCADE,
    type_name   TEXT    NOT NULL CHECK (type_name IN (
                                            'mailbox','email',
                                            'calendar','calendarevent',
                                            'addressbook','contactcard')),
    graph_id    TEXT    NOT NULL,
    local_id    INTEGER NOT NULL,
    PRIMARY KEY (source_id, type_name, graph_id),
    UNIQUE (source_id, type_name, local_id)
);

CREATE INDEX IF NOT EXISTS sync_id_exchange_graph_type_idx
    ON sync_id_exchange_graph (source_id, type_name);

CREATE TABLE IF NOT EXISTS mailboxes (
    id             INTEGER PRIMARY KEY,
    name           TEXT    NOT NULL,
    parent_id      INTEGER REFERENCES mailboxes(id) ON DELETE RESTRICT,
    role           TEXT,
    sort_order     INTEGER NOT NULL DEFAULT 0,
    is_subscribed  INTEGER NOT NULL DEFAULT 1,
    UNIQUE (parent_id, name)
);

CREATE TABLE IF NOT EXISTS emails (
    id           INTEGER PRIMARY KEY,
    blob_id      INTEGER NOT NULL REFERENCES blobs(id),
    received_at  TEXT    NOT NULL,
    mailbox_ids  TEXT    NOT NULL CHECK (json_valid(mailbox_ids)),
    keywords     TEXT    NOT NULL DEFAULT '[]' CHECK (json_valid(keywords)),
    message_match TEXT   NOT NULL DEFAULT '{}' CHECK (json_valid(message_match))
);

CREATE INDEX IF NOT EXISTS emails_blob_idx ON emails (blob_id);

CREATE TABLE IF NOT EXISTS identities (
    id              INTEGER PRIMARY KEY,
    name            TEXT    NOT NULL DEFAULT '',
    email           TEXT    NOT NULL,
    reply_to        TEXT    CHECK (reply_to IS NULL OR json_valid(reply_to)),
    bcc             TEXT    CHECK (bcc IS NULL OR json_valid(bcc)),
    text_signature  TEXT    NOT NULL DEFAULT '',
    html_signature  TEXT    NOT NULL DEFAULT ''
);

CREATE TABLE IF NOT EXISTS sieve_scripts (
    id         INTEGER PRIMARY KEY,
    name       TEXT,
    is_active  INTEGER NOT NULL DEFAULT 0,
    blob_id    INTEGER NOT NULL REFERENCES blobs(id),
    UNIQUE (name)
);

CREATE UNIQUE INDEX IF NOT EXISTS sieve_scripts_one_active_idx
    ON sieve_scripts (is_active) WHERE is_active = 1;

CREATE TABLE IF NOT EXISTS address_books (
    id             INTEGER PRIMARY KEY,
    name           TEXT    NOT NULL,
    description    TEXT,
    sort_order     INTEGER NOT NULL DEFAULT 0,
    is_default     INTEGER NOT NULL DEFAULT 0,
    is_subscribed  INTEGER NOT NULL DEFAULT 1
);

CREATE TABLE IF NOT EXISTS contact_cards (
    id                INTEGER PRIMARY KEY,
    uid               TEXT    NOT NULL UNIQUE,
    address_book_ids  TEXT    NOT NULL CHECK (json_valid(address_book_ids)),
    data              TEXT    NOT NULL CHECK (json_valid(data))
);

CREATE TABLE IF NOT EXISTS calendars (
    id                            INTEGER PRIMARY KEY,
    name                          TEXT    NOT NULL,
    description                   TEXT,
    color                         TEXT,
    sort_order                    INTEGER NOT NULL DEFAULT 0,
    is_subscribed                 INTEGER NOT NULL DEFAULT 1,
    is_visible                    INTEGER NOT NULL DEFAULT 1,
    is_default                    INTEGER NOT NULL DEFAULT 0,
    include_in_availability       TEXT    NOT NULL DEFAULT 'all'
                                      CHECK (include_in_availability IN ('all','attending','none')),
    default_alerts_with_time      TEXT    CHECK (default_alerts_with_time IS NULL
                                                  OR json_valid(default_alerts_with_time)),
    default_alerts_without_time   TEXT    CHECK (default_alerts_without_time IS NULL
                                                  OR json_valid(default_alerts_without_time)),
    time_zone                     TEXT
);

CREATE TABLE IF NOT EXISTS calendar_events (
    id                  INTEGER PRIMARY KEY,
    calendar_ids        TEXT    NOT NULL CHECK (json_valid(calendar_ids)),
    is_draft            INTEGER NOT NULL DEFAULT 0,
    use_default_alerts  INTEGER NOT NULL DEFAULT 0,
    data                TEXT    NOT NULL CHECK (json_valid(data)),
    data_type           TEXT    NOT NULL DEFAULT 'Event'
                         CHECK (data_type IN ('Event','Task','Note','Group'))
);

CREATE TABLE IF NOT EXISTS participant_identities (
    id                INTEGER PRIMARY KEY,
    name              TEXT    NOT NULL DEFAULT '',
    calendar_address  TEXT    NOT NULL,
    is_default        INTEGER NOT NULL DEFAULT 0
);

CREATE TABLE IF NOT EXISTS file_nodes (
    id             INTEGER PRIMARY KEY,
    parent_id      INTEGER REFERENCES file_nodes(id) ON DELETE RESTRICT,
    node_type      TEXT    NOT NULL
                          CHECK (node_type IN ('file','directory','symlink')),
    blob_id        INTEGER REFERENCES blobs(id),
    target         TEXT    CHECK (target IS NULL OR json_valid(target)),
    name           TEXT    NOT NULL,
    media_type     TEXT,
    created        TEXT    NOT NULL,
    modified       TEXT,
    is_subscribed  INTEGER NOT NULL DEFAULT 1,
    role           TEXT,
    UNIQUE (parent_id, name)
);

CREATE INDEX IF NOT EXISTS file_nodes_parent_idx ON file_nodes (parent_id);
CREATE INDEX IF NOT EXISTS file_nodes_blob_idx ON file_nodes (blob_id);

CREATE TABLE IF NOT EXISTS sync_id_takeout (
    source_id      INTEGER NOT NULL REFERENCES sources(id) ON DELETE CASCADE,
    type_name      TEXT    NOT NULL CHECK (type_name IN (
                                              'mailbox','email',
                                              'addressbook','contactcard',
                                              'calendar','calendarevent')),
    source_obj_id  TEXT    NOT NULL,
    local_id       INTEGER NOT NULL,
    PRIMARY KEY (source_id, type_name, source_obj_id),
    UNIQUE (source_id, type_name, local_id)
);

CREATE INDEX IF NOT EXISTS sync_id_takeout_type_idx
    ON sync_id_takeout (source_id, type_name);
