using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Database.Implementations;
using Microsoft.EntityFrameworkCore;

namespace Jellyfin.Server.Implementations.Migrations;

/// <summary>
/// Plain-SQL tracker for the EF migrations history table.
/// Replaces the EF-internal IHistoryRepository / SnakeCaseHistoryRepository.
/// </summary>
public static class MigrationTracker
{
    /// <summary>
    /// Entry name used when writing/reading migration history from backup archives.
    /// Kept as "HistoryRow" for backward compatibility with existing backup files.
    /// </summary>
    public const string BackupEntryName = "HistoryRow";

    private const string CreateTableSql = """
        CREATE TABLE IF NOT EXISTS public."__EFMigrationsHistory" (
            migration_id    character varying(150) NOT NULL,
            product_version character varying(32)  NOT NULL,
            CONSTRAINT "PK___EFMigrationsHistory" PRIMARY KEY (migration_id)
        )
        """;

    /// <summary>Ensures the migrations history table exists.</summary>
    /// <param name="dbContext">The database context.</param>
    /// <param name="ct">Cancellation token.</param>
    /// <returns>A <see cref="Task"/> representing the operation.</returns>
    public static async Task EnsureTableExistsAsync(JellyfinDbContext dbContext, CancellationToken ct = default)
        => await dbContext.Database.ExecuteSqlRawAsync(CreateTableSql, ct).ConfigureAwait(false);

    /// <summary>Returns the migration_id of every applied migration.</summary>
    /// <param name="dbContext">The database context.</param>
    /// <param name="ct">Cancellation token.</param>
    /// <returns>A read-only list of applied migration identifiers.</returns>
    public static async Task<IReadOnlyList<string>> GetAppliedMigrationIdsAsync(JellyfinDbContext dbContext, CancellationToken ct = default)
        => await dbContext.Database
            .SqlQueryRaw<string>("""SELECT migration_id AS "Value" FROM public."__EFMigrationsHistory" """)
            .ToListAsync(ct).ConfigureAwait(false);

    /// <summary>Returns all applied migration rows.</summary>
    /// <param name="dbContext">The database context.</param>
    /// <param name="ct">Cancellation token.</param>
    /// <returns>A read-only list of <see cref="MigrationRecord"/> instances.</returns>
    public static async Task<IReadOnlyList<MigrationRecord>> GetAppliedMigrationsAsync(JellyfinDbContext dbContext, CancellationToken ct = default)
        => await dbContext.Database
            .SqlQueryRaw<MigrationRecord>("""
                SELECT migration_id AS "MigrationId", product_version AS "ProductVersion"
                FROM public."__EFMigrationsHistory"
                """)
            .ToListAsync(ct).ConfigureAwait(false);

    /// <summary>Inserts a migration row, ignoring conflicts.</summary>
    /// <param name="dbContext">The database context.</param>
    /// <param name="migrationId">The migration identifier.</param>
    /// <param name="productVersion">The product version.</param>
    /// <param name="ct">Cancellation token.</param>
    /// <returns>A <see cref="Task"/> representing the operation.</returns>
    public static async Task InsertAsync(JellyfinDbContext dbContext, string migrationId, string productVersion, CancellationToken ct = default)
        => await dbContext.Database.ExecuteSqlAsync(
            $"""
            INSERT INTO public."__EFMigrationsHistory" (migration_id, product_version)
            VALUES ({migrationId}, {productVersion}) ON CONFLICT DO NOTHING
            """,
            ct).ConfigureAwait(false);

    /// <summary>Deletes a migration row by id.</summary>
    /// <param name="dbContext">The database context.</param>
    /// <param name="migrationId">The migration identifier to delete.</param>
    /// <param name="ct">Cancellation token.</param>
    /// <returns>A <see cref="Task"/> representing the operation.</returns>
    public static async Task DeleteAsync(JellyfinDbContext dbContext, string migrationId, CancellationToken ct = default)
        => await dbContext.Database.ExecuteSqlAsync(
            $"""DELETE FROM public."__EFMigrationsHistory" WHERE migration_id = {migrationId}""",
            ct).ConfigureAwait(false);
}
