/*
 * Copyright (C) 2022-2025 Seagull Developers
 *
 * Seagull is the legal property of its developers, whose names are too
 * numerous to list here. Please refer to the AUTHORS file distributed
 * with this source distribution
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <https://www.gnu.org/licenses/>.
 */

#if !defined(SEAGULL_GLOBAL_HEADER_INSIDE) && !defined(SEAGULL_COMPILATION)
# error "only <seagull.h> may be included directly"
#endif

#ifndef SEAGULL_MIGRATIONS_H
#define SEAGULL_MIGRATIONS_H

#include <glib.h>
#include <gio/gio.h>

#include "seagulltypes.h"
#include "seagullversion.h"

G_BEGIN_DECLS

#define SEAGULL_MIGRATIONS_ERROR seagull_migrations_error_quark()

/**
 * SeagullMigrationsError:
 * @SEAGULL_MIGRATIONS_ERROR_EXECUTION_FAILED: the migration failed to execute
 * @SEAGULL_MIGRATIONS_ERROR_PREPARED_STATEMENT_FAILED: failed to create a
 *  prepared statement
 * @SEAGULL_MIGRATIONS_ERROR_PRAGMA_VERSION_FAILED: pragma version did not
 *  return a result
 * @SEAGULL_MIGRATIONS_ERROR_SCHEMA_TOO_NEW: the schema is newer than expected
 * @SEAGULL_MIGRATIONS_ERROR_FAILED_TO_LOAD_RESOURCE: failed to load the
 *  resource
 *
 * Error codes returned when running migrations.
 *
 * Since: 0.1
 */
typedef enum {
	SEAGULL_MIGRATIONS_ERROR_EXECUTION_FAILED SEAGULL_AVAILABLE_ENUMERATOR_IN_0_1,
	SEAGULL_MIGRATIONS_ERROR_PREPARED_STATEMENT_FAILED SEAGULL_AVAILABLE_ENUMERATOR_IN_0_1,
	SEAGULL_MIGRATIONS_ERROR_PRAGMA_VERSION_FAILED SEAGULL_AVAILABLE_ENUMERATOR_IN_0_1,
	SEAGULL_MIGRATIONS_ERROR_SCHEMA_TOO_NEW SEAGULL_AVAILABLE_ENUMERATOR_IN_0_1,
	SEAGULL_MIGRATIONS_ERROR_FAILED_TO_LOAD_RESOURCE SEAGULL_AVAILABLE_ENUMERATOR_IN_0_1,
} SeagullMigrationsError;

/**
 * seagull_migrations_error_quark:
 *
 * The error domain to identify errors with migrations.
 *
 * Returns: The error domain.
 *
 * Since: 0.1
 */
SEAGULL_AVAILABLE_IN_0_1
GQuark seagull_migrations_error_quark(void);

/**
 * seagull_migrations_get_schema_version:
 * @db: the sqlite3 connection
 * @error: (out) (nullable): a return address for a #GError
 *
 * Attempts to read the result of `PRAGMA user_version` which this API uses to
 * store the schema version.
 *
 * Returns: true on success; otherwise false with @error set.
 *
 * Since: 0.1
 */
SEAGULL_AVAILABLE_IN_0_1
int seagull_migrations_get_schema_version(SeagullSqlite3 *db, GError **error);

/**
 * seagull_migrations_run_from_strings:
 * @db: the sqlite3 connection
 * @migrations: (array zero-terminated=1): a list of SQL statements, each item
 *              being its own migration
 * @error: (out) (nullable): a return address for a #GError
 *
 * Runs the given migrations in the order they are given. The index of each
 * migration plus 1 is assumed is to be the version number of the migration,
 * which means that you can not change the order of the migrations. The
 * reasoning for the addition of 1 is because `PRAGMA user_version` defaults to
 * 0.
 *
 * This expects each string in @migrations to be a complete migration. That is,
 * each string in the array should contain all of the SQL statements for that
 * migration. For example, if you're expecting to have 2 migrations, the
 * initial creating two tables, and then adding a column to one of the
 * existing tables, you would have something like the following code.
 *
 * ```c
 * const char *migrations[] = {
 *     // Our initial migration that creates user and session tables.
 *     "CREATE TABLE user(id INTEGER PRIMARY KEY, name TEXT);"
 *     "CREATE TABLE session(user INTEGER, token TEXT) FOREIGN KEY(user) REFERENCES user(id);",
 *     // Begin our second migration that will add a display name to the user
 *     // table. Note the ',' at the end of the previous line.
 *     "ALTER TABLE user ADD COLUMN(display_name TEXT);",
 *     NULL
 * };
 * ```
 *
 * Also, this function will run each migration in its own transaction so you
 * don't need to worry about them. This is done to make sure that the database
 * stays at a known version and an incomplete migration will not be saved.
 *
 * Returns: true on success; otherwise false with @error potentially set.
 *
 * Since: 0.1
 */
SEAGULL_AVAILABLE_IN_0_1
gboolean seagull_migrations_run_from_strings(SeagullSqlite3 *db, const char *migrations[], GError **error);

/**
 * seagull_migrations_run_from_resources:
 * @db: the sqlite3 connection
 * @path: the base path of the resource to use
 * @migrations: (array zero-terminated=1): The list of migrations in the order
 *              to run them.
 * @error: (out) (nullable): a return address for a #GError
 *
 * Runs the given migrations in the order they are given. The index of each
 * migration plus 1 is assumed to be the version number of the migration, which
 * means that you can not change the order of the migrations. The reasoning for
 * the addition of 1 is because `PRAGMA user_version` defaults to 0.
 *
 * This will attempt to load the migrations via
 * [func@Gio.resources_open_stream] by concatenating @path and the individual
 * items of @migrations. Each migration will be ran in a transaction that
 * includes updating the schema version, which is stored in
 * `PRAGMA user_version`. This means you can't use `PRAGMA user_version` for
 * other things.
 *
 * Returns: true on success; otherwise false with @error potentially set.
 *
 * Since: 0.1
 */
SEAGULL_AVAILABLE_IN_0_1
gboolean seagull_migrations_run_from_resources(SeagullSqlite3 *db, const char *path, const char *migrations[], GError **error);

G_END_DECLS

#endif /* SEAGULL_MIGRATIONS_H */
