BareGit
#include "migrations.hpp"

#include <array>

#include <mw/database.hpp>
#include <mw/error.hpp>
#include <mw/utils.hpp>

namespace overseer::db
{

namespace
{

// One element per SQL statement. CREATE TRIGGER bodies contain
// semicolons (between statements in their BEGIN…END block), so we keep
// each trigger as a single string here rather than splitting on ';'.
constexpr std::array<const char*, 17> SCHEMA_V1_STATEMENTS = {
    // ---- attachment ---------------------------------------------------
    R"SQL(
CREATE TABLE attachment
(
    id      INTEGER PRIMARY KEY AUTOINCREMENT,
    mime    TEXT    NOT NULL,
    sha256  TEXT    NOT NULL UNIQUE,
    bytes   BLOB    NOT NULL
);
)SQL",

    // ---- storage ------------------------------------------------------
    R"SQL(
CREATE TABLE storage
(
    id            INTEGER PRIMARY KEY AUTOINCREMENT,
    parent_id     INTEGER NULL REFERENCES storage(id) ON DELETE RESTRICT,
    name          TEXT    NOT NULL,
    description   TEXT    NULL,
    attachment_id INTEGER NULL REFERENCES attachment(id) ON DELETE SET NULL,
    UNIQUE(parent_id, name)
);
)SQL",
    R"SQL(
CREATE UNIQUE INDEX storage_root_name_unique
    ON storage(name) WHERE parent_id IS NULL;
)SQL",
    "CREATE INDEX storage_parent_idx ON storage(parent_id);",

    // ---- stuff --------------------------------------------------------
    R"SQL(
CREATE TABLE stuff
(
    id            INTEGER PRIMARY KEY AUTOINCREMENT,
    name          TEXT    NOT NULL,
    description   TEXT    NULL,
    attachment_id INTEGER NULL REFERENCES attachment(id) ON DELETE SET NULL,
    storage_id    INTEGER NOT NULL REFERENCES storage(id) ON DELETE RESTRICT
);
)SQL",
    "CREATE INDEX stuff_storage_idx ON stuff(storage_id);",

    // ---- stuff_move + trim trigger ------------------------------------
    R"SQL(
CREATE TABLE stuff_move
(
    id         INTEGER PRIMARY KEY AUTOINCREMENT,
    stuff_id   INTEGER NOT NULL REFERENCES stuff(id) ON DELETE CASCADE,
    storage_id INTEGER NOT NULL REFERENCES storage(id) ON DELETE RESTRICT,
    moved_at   INTEGER NOT NULL
);
)SQL",
    R"SQL(
CREATE INDEX stuff_move_stuff_idx ON stuff_move(stuff_id, moved_at DESC);
)SQL",
    R"SQL(
CREATE TRIGGER stuff_move_trim
AFTER INSERT ON stuff_move
BEGIN
    DELETE FROM stuff_move
    WHERE stuff_id = NEW.stuff_id
      AND id IN (
        SELECT id FROM stuff_move
         WHERE stuff_id = NEW.stuff_id
         ORDER BY moved_at DESC, id DESC
         LIMIT -1 OFFSET 10
      );
END;
)SQL",

    // ---- FTS5 virtual tables ------------------------------------------
    R"SQL(
CREATE VIRTUAL TABLE storage_fts USING fts5(
    name, description,
    content='storage', content_rowid='id',
    tokenize='unicode61 remove_diacritics 2'
);
)SQL",
    R"SQL(
CREATE VIRTUAL TABLE stuff_fts USING fts5(
    name, description,
    content='stuff', content_rowid='id',
    tokenize='unicode61 remove_diacritics 2'
);
)SQL",

    // ---- FTS sync triggers --------------------------------------------
    R"SQL(
CREATE TRIGGER storage_ai AFTER INSERT ON storage BEGIN
    INSERT INTO storage_fts(rowid, name, description)
    VALUES (NEW.id, NEW.name, NEW.description);
END;
)SQL",
    R"SQL(
CREATE TRIGGER storage_ad AFTER DELETE ON storage BEGIN
    INSERT INTO storage_fts(storage_fts, rowid, name, description)
    VALUES ('delete', OLD.id, OLD.name, OLD.description);
END;
)SQL",
    R"SQL(
CREATE TRIGGER storage_au AFTER UPDATE ON storage BEGIN
    INSERT INTO storage_fts(storage_fts, rowid, name, description)
    VALUES ('delete', OLD.id, OLD.name, OLD.description);
    INSERT INTO storage_fts(rowid, name, description)
    VALUES (NEW.id, NEW.name, NEW.description);
END;
)SQL",
    R"SQL(
CREATE TRIGGER stuff_ai AFTER INSERT ON stuff BEGIN
    INSERT INTO stuff_fts(rowid, name, description)
    VALUES (NEW.id, NEW.name, NEW.description);
END;
)SQL",
    R"SQL(
CREATE TRIGGER stuff_ad AFTER DELETE ON stuff BEGIN
    INSERT INTO stuff_fts(stuff_fts, rowid, name, description)
    VALUES ('delete', OLD.id, OLD.name, OLD.description);
END;
)SQL",
    R"SQL(
CREATE TRIGGER stuff_au AFTER UPDATE ON stuff BEGIN
    INSERT INTO stuff_fts(stuff_fts, rowid, name, description)
    VALUES ('delete', OLD.id, OLD.name, OLD.description);
    INSERT INTO stuff_fts(rowid, name, description)
    VALUES (NEW.id, NEW.name, NEW.description);
END;
)SQL",
};

} // namespace

mw::E<void> migrateDB0To1(mw::SQLite& db)
{
    for(const char* stmt : SCHEMA_V1_STATEMENTS)
    {
        DO_OR_RETURN(db.execute(stmt));
    }
    return {};
}

} // namespace overseer::db