using ErsatzTV.Application.Auth;
using ErsatzTV.Core;
using LanguageExt;
using MediatR;

namespace ErsatzTV.Services.RunOnce;

/// <summary>
///     Recovery/bootstrap: when <c>Auth:LocalAdmin:Password</c> is configured, (re)seeds the single local
///     administrator at startup (issue #295). Overwrites any existing credential and rotates the security
///     stamp, so an operator locked out of the browser UI can reset by setting the env and restarting. A
///     no-op when unset. Follows the RunOnce pattern (waits for the database to be ready — the migrator is a
///     BackgroundService, so registration order alone does not guarantee the schema exists).
/// </summary>
public class LocalAdminSeedService(
    IServiceScopeFactory serviceScopeFactory,
    IConfiguration configuration,
    SystemStartup systemStartup,
    ILogger<LocalAdminSeedService> logger) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        await Task.Yield();

        string password = configuration["Auth:LocalAdmin:Password"];
        if (string.IsNullOrWhiteSpace(password))
        {
            return;
        }

        await systemStartup.WaitForDatabase(stoppingToken);
        if (stoppingToken.IsCancellationRequested)
        {
            return;
        }

        string username = configuration["Auth:LocalAdmin:Username"];

        try
        {
            using IServiceScope scope = serviceScopeFactory.CreateScope();
            IMediator mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
            Either<BaseError, Unit> result =
                await mediator.Send(new SeedLocalAdminFromEnvironment(username, password), stoppingToken);

            result.Match(
                Right: _ => logger.LogWarning(
                    "Seeded the local administrator from Auth:LocalAdmin:* configuration (any existing "
                    + "credential was overwritten and all sessions revoked). Unset Auth:LocalAdmin:Password "
                    + "after signing in."),
                Left: error => logger.LogError(
                    "Failed to seed the local administrator from configuration: {Error}",
                    error.Value));
        }
        catch (Exception ex)
        {
            logger.LogError(ex, "Failed to seed the local administrator from configuration");
        }
    }
}
