using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Design;

namespace DodoSSH.Infrastructure;

/// <summary>
/// Builds a context for <c>dotnet ef</c> at design time.
/// </summary>
/// <remarks>
/// Deliberately independent of the API host: generating or applying a migration should not require the web
/// application to start. The API never migrates in production — it fails readiness instead — so this is the
/// one place that knows how to open the database without a web host, and the end-to-end suite uses it for
/// the same reason <c>dotnet ef</c> does.
/// </remarks>
public sealed class DodoDbContextFactory : IDesignTimeDbContextFactory<DodoDbContext>
{
    /// <summary>Environment variable naming the database to work against.</summary>
    /// <remarks>
    /// Only <c>database update</c> and <c>dbcontext script</c> actually connect. Generating a migration
    /// needs a provider but not a reachable server, which is why the default below is allowed to be a
    /// guess at all.
    /// </remarks>
    public const string ConnectionVariable = "DODOSSH_DESIGN_CONNECTION";

    /// <summary>
    /// Where <c>dotnet ef</c> connects when nothing says otherwise.
    /// </summary>
    /// <remarks>
    /// The development compose stack, because that is the only local database this repository defines. A
    /// default naming a server nobody creates makes the documented <c>dotnet ef database update</c> fail on
    /// every clean machine, which is a poor first impression of a self-hosted product — and the failure
    /// arrives as a SCRAM authentication error, which reads like a broken stack rather than a stale
    /// default. These credentials are the compose file's own; a real deployment migrates through
    /// <see cref="ConnectionVariable"/> or the migrator job.
    /// </remarks>
    private const string DevelopmentStackConnection =
        "Host=localhost;Port=5432;Database=dodossh;Username=dodossh;Password=dodossh";

    /// <inheritdoc />
    public DodoDbContext CreateDbContext(string[] args) =>
        Create(Environment.GetEnvironmentVariable(ConnectionVariable) ?? DevelopmentStackConnection);

    /// <summary>
    /// Builds a context for one database, configured as the API configures its own.
    /// </summary>
    /// <param name="connectionString">Where to connect.</param>
    /// <remarks>
    /// The migrations history table has to be placed exactly where <c>AddDodoPersistence</c> looks for it.
    /// Defined here once so that a schema applied outside the API is a schema the API agrees is current,
    /// rather than one it reports as entirely pending.
    /// </remarks>
    public static DodoDbContext Create(string connectionString)
    {
        var options = new DbContextOptionsBuilder<DodoDbContext>()
            .UseNpgsql(connectionString, npgsql =>
                npgsql.MigrationsHistoryTable("__EFMigrationsHistory", DodoDbContext.SchemaName))
            .UseSnakeCaseNamingConvention()
            .Options;

        return new DodoDbContext(options);
    }
}
