using Microsoft.EntityFrameworkCore;
using RpgRoller.Data;

namespace RpgRoller.Hosting;

public static class SqliteSchemaUpgrader
{
    public static void ApplyPendingChanges(RpgRollerDbContext db)
    {
        if (db.Database.IsSqlite())
        {
            db.Database.OpenConnection();
            try
            {
                EnsureLegacySchemaHistory(db);
                EnsureSplitMigrationHistory(db);
            }
            finally
            {
                db.Database.CloseConnection();
            }
        }

        db.Database.Migrate();
    }

    private static void EnsureLegacySchemaHistory(RpgRollerDbContext db)
    {
        if (TableExists(db, "__EFMigrationsHistory"))
            return;

        if (!TableExists(db, "Skills"))
            return;

        if (!ColumnExists(db, "Skills", "WildDice") || !ColumnExists(db, "Skills", "AllowFumble"))
            return;

        using var createHistoryCommand = db.Database.GetDbConnection().CreateCommand();
        createHistoryCommand.CommandText = """
                                           CREATE TABLE IF NOT EXISTS "__EFMigrationsHistory" (
                                               "MigrationId" TEXT NOT NULL CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY,
                                               "ProductVersion" TEXT NOT NULL
                                           );
                                           """;
        _ = createHistoryCommand.ExecuteNonQuery();

        InsertMigrationHistory(db, InitialMigrationId);
    }

    private static void EnsureSplitMigrationHistory(RpgRollerDbContext db)
    {
        if (!TableExists(db, "__EFMigrationsHistory"))
            return;

        if (!MigrationExists(db, CharactersCampaignDeletionMigrationId))
            return;

        if (MigrationExists(db, AuthorizationRolesMigrationId))
            return;

        if (!ColumnExists(db, "Users", "Roles"))
            return;

        InsertMigrationHistory(db, AuthorizationRolesMigrationId);
    }

    private static bool TableExists(RpgRollerDbContext db, string tableName)
    {
        using var command = db.Database.GetDbConnection().CreateCommand();
        command.CommandText = "SELECT 1 FROM sqlite_master WHERE type='table' AND name=$name LIMIT 1;";
        var parameter = command.CreateParameter();
        parameter.ParameterName = "$name";
        parameter.Value = tableName;
        command.Parameters.Add(parameter);

        return command.ExecuteScalar() is not null;
    }

    private static bool ColumnExists(RpgRollerDbContext db, string tableName, string columnName)
    {
        using var command = db.Database.GetDbConnection().CreateCommand();
        command.CommandText = $"PRAGMA table_info('{tableName}');";
        using var reader = command.ExecuteReader();
        while (reader.Read())
        {
            var currentColumnName = reader.GetString(1);
            if (string.Equals(currentColumnName, columnName, StringComparison.OrdinalIgnoreCase))
                return true;
        }

        return false;
    }

    private static bool MigrationExists(RpgRollerDbContext db, string migrationId)
    {
        using var command = db.Database.GetDbConnection().CreateCommand();
        command.CommandText = """
                              SELECT 1
                              FROM "__EFMigrationsHistory"
                              WHERE "MigrationId" = $migrationId
                              LIMIT 1;
                              """;
        var migrationParameter = command.CreateParameter();
        migrationParameter.ParameterName = "$migrationId";
        migrationParameter.Value = migrationId;
        command.Parameters.Add(migrationParameter);

        return command.ExecuteScalar() is not null;
    }

    private static void InsertMigrationHistory(RpgRollerDbContext db, string migrationId)
    {
        using var insertHistoryCommand = db.Database.GetDbConnection().CreateCommand();
        insertHistoryCommand.CommandText = """
                                           INSERT OR IGNORE INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
                                           VALUES ($migrationId, $productVersion);
                                           """;

        var migrationParameter = insertHistoryCommand.CreateParameter();
        migrationParameter.ParameterName = "$migrationId";
        migrationParameter.Value = migrationId;
        insertHistoryCommand.Parameters.Add(migrationParameter);

        var productVersionParameter = insertHistoryCommand.CreateParameter();
        productVersionParameter.ParameterName = "$productVersion";
        productVersionParameter.Value = ProductVersion;
        insertHistoryCommand.Parameters.Add(productVersionParameter);

        _ = insertHistoryCommand.ExecuteNonQuery();
    }

    private const string InitialMigrationId = "20260226084000_InitialSchema";
    private const string CharactersCampaignDeletionMigrationId = "20260226160859_AddAuthorizationRolesAndCampaignDeletion";
    private const string AuthorizationRolesMigrationId = "20260226170000_AddAuthorizationRoles";
    private const string ProductVersion = "10.0.2";
}