using Eis.Infrastructure.Data;
using Microsoft.Data.Sqlite;

namespace Eis.Infrastructure.Tests.Data;

public sealed class DatabaseMaintenanceServiceTests
{
    [Fact]
    public async Task SeedAndReset_UseValidatedReplacementAndCreateBackups()
    {
        var root = Path.Combine(Path.GetTempPath(), $"eis-tools-{Guid.NewGuid():N}");
        var path = Path.Combine(root, "eis.sqlite");
        Directory.CreateDirectory(root);
        try
        {
            var service = new DatabaseMaintenanceService(DatabaseOptions.CreateSqlite(path));
            await service.ExecuteAsync(
                DatabaseMaintenanceMode.Init,
                null,
                force: false,
                dryRun: false,
                CancellationToken.None);

            var seed = await service.ExecuteAsync(
                DatabaseMaintenanceMode.Seed,
                path,
                force: false,
                dryRun: false,
                CancellationToken.None);
            Assert.NotNull(seed.BackupPath);
            Assert.True(File.Exists(seed.BackupPath));
            Assert.Equal(1200, seed.Summary?.Candidates);
            Assert.Equal(10800, seed.Summary?.Results);
            Assert.Equal(1200, await ScalarAsync(path, "SELECT COUNT(*) FROM candidate_profiles"));
            Assert.Equal(10800, await ScalarAsync(path, "SELECT COUNT(*) FROM results"));

            var reset = await service.ExecuteAsync(
                DatabaseMaintenanceMode.Reset,
                path,
                force: false,
                dryRun: false,
                CancellationToken.None);
            Assert.NotNull(reset.BackupPath);
            Assert.True(File.Exists(reset.BackupPath));
            Assert.Equal(0, await ScalarAsync(path, "SELECT COUNT(*) FROM candidate_profiles"));
            Assert.Equal(1, await ScalarAsync(path, "SELECT COUNT(*) FROM users"));
            Assert.Equal(20, await ScalarAsync(path,
                "SELECT schema_version FROM schema_metadata WHERE id = 1"));
        }
        finally
        {
            SqliteConnection.ClearAllPools();
            if (Directory.Exists(root))
            {
                Directory.Delete(root, recursive: true);
            }
        }
    }

    [Fact]
    public async Task DestructiveCommand_RequiresExactTargetConfirmation()
    {
        var root = Path.Combine(Path.GetTempPath(), $"eis-tools-confirm-{Guid.NewGuid():N}");
        var path = Path.Combine(root, "eis.sqlite");
        try
        {
            var service = new DatabaseMaintenanceService(DatabaseOptions.CreateSqlite(path));
            var exception = await Assert.ThrowsAsync<InvalidOperationException>(() =>
                service.ExecuteAsync(
                    DatabaseMaintenanceMode.Seed,
                    path + ".wrong",
                    force: false,
                    dryRun: false,
                    CancellationToken.None));

            Assert.Contains("--confirm-target", exception.Message);
            Assert.False(File.Exists(path));
        }
        finally
        {
            SqliteConnection.ClearAllPools();
            if (Directory.Exists(root))
            {
                Directory.Delete(root, recursive: true);
            }
        }
    }

    [Fact]
    public async Task DryRun_ReportsSeedWithoutWritingDatabase()
    {
        var root = Path.Combine(Path.GetTempPath(), $"eis-tools-dry-{Guid.NewGuid():N}");
        var path = Path.Combine(root, "eis.sqlite");
        var service = new DatabaseMaintenanceService(DatabaseOptions.CreateSqlite(path));

        var result = await service.ExecuteAsync(
            DatabaseMaintenanceMode.Seed,
            null,
            force: false,
            dryRun: true,
            CancellationToken.None);

        Assert.True(result.DryRun);
        Assert.Equal(1200, result.Summary?.Candidates);
        Assert.False(File.Exists(path));
    }

    [Fact]
    public async Task MySqlSystemDatabase_IsRejectedBeforeConnecting()
    {
        var options = DatabaseOptions.CreateMySql(
            "Server=127.0.0.1;User ID=test;Database=mysql;Connection Timeout=1");
        var service = new DatabaseMaintenanceService(options);

        var exception = await Assert.ThrowsAsync<InvalidOperationException>(() =>
            service.ExecuteAsync(
                DatabaseMaintenanceMode.Init,
                null,
                force: false,
                dryRun: true,
                CancellationToken.None));

        Assert.Contains("系统数据库", exception.Message);
    }

    private static async Task<long> ScalarAsync(string path, string sql)
    {
        await using var connection = new SqliteConnection($"Data Source={path};Pooling=False");
        await connection.OpenAsync();
        await using var command = connection.CreateCommand();
        command.CommandText = sql;
        return Convert.ToInt64(await command.ExecuteScalarAsync());
    }
}
