-- PROJ: external-share-links
-- Doku-only (siehe README.md in diesem Verzeichnis) — die tatsächliche
-- Ausführung passiert idempotent über internal/storage/shares.go
-- initSharesSchema(), aufgerufen aus (*Store).New().
--
-- Externe Share-Links: tenant-scoped, ablaufender, optional passwortgeschützter
-- öffentlicher Link auf genau EIN Dokument.
--   * Token: 32 Byte crypto/rand, base64url; wird beim Erzeugen genau EINMAL
--     zurückgegeben. Persistiert wird NUR der SHA-256-Hash (token_hash). Der
--     öffentliche Abruf sucht immer über token_hash, nie über id.
--   * expires_at ist Pflicht (kein unbegrenzter Share). password_hash optional
--     (bcrypt, Cost 12).
--   * Kein Hard-Delete: Revoke setzt nur revoked_at/revoked_by.
--   * Prüfreihenfolge beim öffentlichen Abruf: revoked -> expired ->
--     max_accesses erreicht -> Passwort -> ausliefern (access_count++ atomar
--     mit WHERE-Guard gegen Race). Datei wird serverseitig aus dem WORM-Store
--     gestreamt; storage_path/content_hash NIE im Response.
--   * Jeder Zugriffsversuch (Erfolg/Fehlschlag) landet in
--     document_share_accesses mit passendem result-Wert; die öffentlichen
--     Endpunkte sind zusätzlich per-IP rate-limited (Token-Bucket in-memory).
--
-- Hinweis: tenant_id / created_by / revoked_by tragen bewusst KEINE FK auf
-- tenants(id) bzw. users(id) — konsistent mit documents/taxonomy/permissions
-- (plain BIGINT) und weil tenants/users von anderen Stores NACH storage.New()
-- angelegt werden (Reihenfolge in cmd/archivdms/main.go). document_id behält
-- seine FK, da documents die eigene Tabelle dieses Stores ist.

CREATE TABLE IF NOT EXISTS document_shares (
    id             BIGSERIAL PRIMARY KEY,
    tenant_id      BIGINT NOT NULL,
    document_id    BIGINT NOT NULL REFERENCES documents(id) ON DELETE CASCADE,
    token_hash     TEXT NOT NULL UNIQUE,
    created_by     BIGINT NOT NULL,
    created_at     TIMESTAMPTZ NOT NULL DEFAULT now(),
    expires_at     TIMESTAMPTZ NOT NULL,
    max_accesses   INT,
    access_count   INT NOT NULL DEFAULT 0,
    password_hash  TEXT,
    revoked_at     TIMESTAMPTZ,
    revoked_by     BIGINT
);
CREATE INDEX IF NOT EXISTS idx_document_shares_document ON document_shares(document_id);
CREATE INDEX IF NOT EXISTS idx_document_shares_tenant ON document_shares(tenant_id);

CREATE TABLE IF NOT EXISTS document_share_accesses (
    id          BIGSERIAL PRIMARY KEY,
    share_id    BIGINT NOT NULL REFERENCES document_shares(id) ON DELETE CASCADE,
    accessed_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    ip_address  INET,
    user_agent  TEXT,
    result      TEXT NOT NULL CHECK (result IN ('success','expired','revoked','max_reached','bad_password','rate_limited'))
);
CREATE INDEX IF NOT EXISTS idx_share_accesses_share ON document_share_accesses(share_id, accessed_at DESC);
