-- ============================================================
-- AEGIS MANIFEST (SQLite) - UPDATED: datastore label visible, inner requires PIN
-- ============================================================

-- ----------------------------
-- PRAGMAS (aplicar na abertura da conexão)
-- ----------------------------
PRAGMA foreign_keys = ON;
PRAGMA journal_mode = WAL;
PRAGMA synchronous = NORMAL;
PRAGMA temp_store = MEMORY;

-- ============================================================
-- USERS (OIDC identities)
-- ============================================================

CREATE TABLE IF NOT EXISTS users (
                                     user_id        TEXT PRIMARY KEY,          -- GUID/ULID (string)
                                     created_at     INTEGER NOT NULL,           -- epoch seconds
                                     disabled_at    INTEGER,                    -- null = active

    -- OIDC identity (stable pairing)
                                     subject        TEXT NOT NULL,              -- "sub"
                                     issuer         TEXT NOT NULL,              -- "iss"

                                     display_name   TEXT,
                                     email          TEXT,

                                     UNIQUE(subject, issuer)
    );

-- ============================================================
-- DATASTORES
-- ============================================================
-- IMPORTANT:
-- - datastore name is decryptable WITHOUT PIN (using Label Key mechanism)
-- - datastore inner content requires PIN (Master Key unwrapped with PIN-derived KEK)

CREATE TABLE IF NOT EXISTS datastores (
                                          datastore_id     TEXT PRIMARY KEY,          -- GUID/ULID
                                          created_at       INTEGER NOT NULL,
                                          deleted_at       INTEGER,

    -- --------------------------
    -- LABEL (visible without PIN)
    -- --------------------------
    -- name_enc is encrypted with a "Label Key" (LK) that does NOT require datastore PIN.
    -- LK can be derived from a server secret, KMS, or any non-PIN mechanism.
                                          name_enc         BLOB NOT NULL,
                                          name_nonce       BLOB NOT NULL,

    -- Identifies which LK mechanism/version was used (for rotation/migration).
    -- Examples: "lk:server:v1", "lk:kms:key/123", "lk:yubikey:piv:9c"
                                          lk_kid           TEXT NOT NULL,
                                          lk_version       INTEGER NOT NULL DEFAULT 1,

    -- --------------------------
    -- MASTER KEY (PIN protected)
    -- --------------------------
    -- mk_wrapped is the datastore Master Key (MK) encrypted ("wrapped") with a KEK derived from the datastore PIN.
                                          mk_wrapped       BLOB NOT NULL,
                                          mk_nonce         BLOB NOT NULL,
                                          mk_version       INTEGER NOT NULL DEFAULT 1,

    -- PIN KDF parameters (so you can change KDF settings per datastore over time)
                                          pin_kdf          TEXT NOT NULL DEFAULT 'argon2id',  -- 'argon2id' recommended
                                          pin_salt         BLOB NOT NULL,
    -- Store params as JSON text (portable and easy to evolve)
    -- Example: {"memory_kib":65536,"iterations":3,"parallelism":1,"output_len":32}
                                          pin_kdf_params   TEXT NOT NULL,

    -- Optional: quick verifier to reduce unwrap attempts (can be null).
    -- If used, store an AEAD-encrypted constant (e.g., "AEGIS_PIN_OK") with KEK_pin and a nonce.
    -- Otherwise you simply "try unwrap MK" and treat failure as "wrong PIN".
                                          pin_verifier_enc   BLOB,
                                          pin_verifier_nonce BLOB,

    -- Root node of the datastore tree
                                          root_node_id     TEXT NOT NULL,

                                          FOREIGN KEY(root_node_id) REFERENCES nodes(node_id)
    );

-- ============================================================
-- DATASTORE MEMBERS (authorization)
-- ============================================================

CREATE TABLE IF NOT EXISTS datastore_members (
                                                 datastore_id   TEXT NOT NULL,
                                                 user_id        TEXT NOT NULL,
                                                 role           TEXT NOT NULL,              -- 'OWNER'|'ADMIN'|'EDITOR'|'VIEWER'
                                                 created_at     INTEGER NOT NULL,

                                                 PRIMARY KEY(datastore_id, user_id),
    FOREIGN KEY(datastore_id) REFERENCES datastores(datastore_id),
    FOREIGN KEY(user_id) REFERENCES users(user_id)
    );

-- ============================================================
-- NODES (logical tree) - INTERNAL: requires PIN
-- ============================================================

CREATE TABLE IF NOT EXISTS nodes (
                                     node_id        TEXT PRIMARY KEY,           -- GUID/ULID
                                     datastore_id   TEXT NOT NULL,
                                     parent_id      TEXT,                       -- null only for root
                                     kind           INTEGER NOT NULL,           -- 1=DIR, 2=FILE
                                     created_at     INTEGER NOT NULL,
                                     updated_at     INTEGER NOT NULL,
                                     deleted_at     INTEGER,                    -- soft delete/trash

    -- Encrypted node name (requires MK -> PIN)
                                     name_enc       BLOB NOT NULL,
                                     name_nonce     BLOB NOT NULL,

    -- File-only fields (kind=FILE)
                                     main_object_id TEXT,                       -- FK objects.object_id
                                     size_plain     INTEGER,                    -- optional (can leak info; keep or encrypt later)
                                     mime_enc       BLOB,                       -- optional: encrypt mime to reduce metadata leak
                                     mime_nonce     BLOB,

                                     version        INTEGER NOT NULL DEFAULT 1, -- optimistic lock

                                     FOREIGN KEY(datastore_id) REFERENCES datastores(datastore_id),
    FOREIGN KEY(parent_id) REFERENCES nodes(node_id),
    FOREIGN KEY(main_object_id) REFERENCES objects(object_id)
    );

-- ============================================================
-- OBJECTS (encrypted blobs) - INTERNAL: requires PIN
-- ============================================================

CREATE TABLE IF NOT EXISTS objects (
                                       object_id      TEXT PRIMARY KEY,           -- GUID/ULID
                                       datastore_id   TEXT NOT NULL,
                                       created_at     INTEGER NOT NULL,

    -- AEAD algorithm info
                                       cipher         TEXT NOT NULL,              -- e.g. 'AES-256-GCM'
                                       nonce          BLOB NOT NULL,              -- base nonce / object nonce (format depends on your file container)

    -- DEK wrapped by datastore MK (requires PIN)
                                       dek_wrapped    BLOB NOT NULL,
                                       dek_nonce      BLOB NOT NULL,

    -- Reference to blob location (also encrypted to avoid path leaks)
                                       blob_ref_enc   BLOB NOT NULL,
                                       blob_ref_nonce BLOB NOT NULL,

                                       size_cipher    INTEGER NOT NULL,
                                       hash_plain     BLOB,                       -- optional (SHA-256) for integrity/dedupe v2

                                       FOREIGN KEY(datastore_id) REFERENCES datastores(datastore_id)
    );

-- ============================================================
-- DERIVATIVES (thumbs/previews/posters/etc.) - INTERNAL: requires PIN
-- ============================================================

CREATE TABLE IF NOT EXISTS derivatives (
                                           derivative_id  TEXT PRIMARY KEY,           -- GUID/ULID
                                           datastore_id   TEXT NOT NULL,
                                           node_id        TEXT NOT NULL,              -- owning file node
                                           created_at     INTEGER NOT NULL,
                                           updated_at     INTEGER NOT NULL,

                                           kind           TEXT NOT NULL,              -- 'THUMB'|'POSTER'|'PREVIEW'|'WAVEFORM'|'SPRITE'...
                                           profile        TEXT NOT NULL,              -- '256w','512w','page1','t=3s','wave:1024',...

                                           object_id      TEXT,                       -- object containing the derivative blob (when READY)
                                           status         INTEGER NOT NULL,           -- 0=PENDING,1=READY,2=FAILED

    -- store error details encrypted (may contain sensitive paths/info)
                                           error_enc      BLOB,
                                           error_nonce    BLOB,

                                           FOREIGN KEY(datastore_id) REFERENCES datastores(datastore_id),
    FOREIGN KEY(node_id) REFERENCES nodes(node_id),
    FOREIGN KEY(object_id) REFERENCES objects(object_id),

    UNIQUE(datastore_id, node_id, kind, profile)
    );

-- ============================================================
-- JOBS (SQLite queue) - does NOT require PIN itself
-- Payload should be encrypted with MK if it contains sensitive info (recommended).
-- ============================================================

CREATE TABLE IF NOT EXISTS jobs (
                                    job_id         TEXT PRIMARY KEY,           -- GUID/ULID
                                    datastore_id   TEXT NOT NULL,
                                    created_at     INTEGER NOT NULL,
                                    available_at   INTEGER NOT NULL,           -- for backoff scheduling
                                    picked_at      INTEGER,
                                    finished_at    INTEGER,

                                    kind           TEXT NOT NULL,              -- 'GEN_DERIVATIVES'|'REWRAP_KEYS' etc.

                                    payload_enc    BLOB NOT NULL,
                                    payload_nonce  BLOB NOT NULL,

                                    status         INTEGER NOT NULL,           -- 0=QUEUED,1=RUNNING,2=DONE,3=FAILED
                                    attempts       INTEGER NOT NULL DEFAULT 0,

                                    FOREIGN KEY(datastore_id) REFERENCES datastores(datastore_id)
    );

-- ============================================================
-- AUDIT LOG (encrypted details) - INTERNAL: may require PIN depending on what you put in details
-- ============================================================

CREATE TABLE IF NOT EXISTS audit_log (
                                         audit_id       TEXT PRIMARY KEY,           -- GUID/ULID
                                         datastore_id   TEXT NOT NULL,
                                         created_at     INTEGER NOT NULL,
                                         actor_user_id  TEXT,                       -- null for system/worker
                                         action         TEXT NOT NULL,              -- 'UPLOAD','RENAME','MOVE','DELETE','DOWNLOAD','UNLOCK','LOCK'...

                                         target_node_id TEXT,
                                         details_enc    BLOB NOT NULL,
                                         details_nonce  BLOB NOT NULL,

                                         FOREIGN KEY(datastore_id) REFERENCES datastores(datastore_id),
    FOREIGN KEY(actor_user_id) REFERENCES users(user_id),
    FOREIGN KEY(target_node_id) REFERENCES nodes(node_id)
    );

-- ============================================================
-- INDEXES (hot paths)
-- ============================================================

-- list directory: by datastore + parent, excluding deleted
CREATE INDEX IF NOT EXISTS idx_nodes_parent
    ON nodes(datastore_id, parent_id, deleted_at);

CREATE INDEX IF NOT EXISTS idx_nodes_parent_kind
    ON nodes(datastore_id, parent_id, kind, deleted_at);

CREATE INDEX IF NOT EXISTS idx_nodes_main_object
    ON nodes(main_object_id);

CREATE INDEX IF NOT EXISTS idx_objects_datastore_created
    ON objects(datastore_id, created_at);

CREATE INDEX IF NOT EXISTS idx_derivatives_node
    ON derivatives(datastore_id, node_id, kind, status);

-- jobs queue
CREATE INDEX IF NOT EXISTS idx_jobs_queue
    ON jobs(status, available_at);

-- members lookup
CREATE INDEX IF NOT EXISTS idx_members_user
    ON datastore_members(user_id);

-- datastore listing for a user typically joins members -> datastores
CREATE INDEX IF NOT EXISTS idx_members_datastore
    ON datastore_members(datastore_id);

-- audit time queries
CREATE INDEX IF NOT EXISTS idx_audit_datastore_time
    ON audit_log(datastore_id, created_at);

-- ============================================================
-- TRIGGERS (touch updated_at / version)
-- NOTE: SQLite default PRAGMA recursive_triggers is OFF, so these won't recurse infinitely.
-- ============================================================

CREATE TRIGGER IF NOT EXISTS trg_nodes_touch
AFTER UPDATE ON nodes
                            FOR EACH ROW
BEGIN
UPDATE nodes
SET updated_at = strftime('%s','now'),
    version = version + 1
WHERE node_id = NEW.node_id;
END;

CREATE TRIGGER IF NOT EXISTS trg_derivatives_touch
AFTER UPDATE ON derivatives
                            FOR EACH ROW
BEGIN
UPDATE derivatives
SET updated_at = strftime('%s','now')
WHERE derivative_id = NEW.derivative_id;
END;
