using Baya.Application.Contracts.Common;
using Baya.Infrastructure.CrossCutting.Seams;
using Microsoft.Extensions.DependencyInjection;

namespace Baya.Infrastructure.CrossCutting.ServiceConfiguration;

public static class DevelopmentSeamExtensions
{
    /// <summary>
    /// Development-only wiring for the OTP bring-up bridge. Registers <see cref="DevOtpStore"/> and decorates
    /// the <see cref="ISmsSender"/> registered by <c>AddCrossCuttingSeams</c> with
    /// <see cref="DevCapturingSmsSender"/>, so each OTP is also captured in memory for
    /// <c>/api/v1/dev/last_otp/{phone}</c> to serve. MUST be called only inside
    /// <c>builder.Environment.IsDevelopment()</c>, and only for a capture-safe provider (the log-only
    /// <see cref="LoggingSmsSender"/> or the Development-only Telegram relay — <c>Program.cs</c> owns that
    /// condition): nothing here is wired in any other environment, which — together with the endpoint's own
    /// <c>IsDevelopment()</c> guard — makes the OTP echo impossible to enable outside Development.
    /// </summary>
    public static IServiceCollection AddDevelopmentOtpCapture(this IServiceCollection services)
    {
        services.AddSingleton<DevOtpStore>();

        // Decorate whatever ISmsSender is already registered rather than assuming the mock — with
        // Seams:Sms:Provider = telegram the inner sender is TelegramSmsSender, and re-creating a LoggingSmsSender
        // here would silently swallow the delivery instead of capturing alongside it. Last registration wins for
        // a single resolve, so callers transparently get the decorator and delivery behaviour is unchanged.
        var inner = services.LastOrDefault(d => d.ServiceType == typeof(ISmsSender))
            ?? throw new InvalidOperationException(
                "AddDevelopmentOtpCapture must run after AddCrossCuttingSeams — no ISmsSender is registered.");
        services.Remove(inner);

        services.AddSingleton<ISmsSender>(sp => new DevCapturingSmsSender(
            ResolveSender(sp, inner),
            sp.GetRequiredService<DevOtpStore>()));

        return services;
    }

    private static ISmsSender ResolveSender(IServiceProvider sp, ServiceDescriptor descriptor) => descriptor switch
    {
        { ImplementationInstance: ISmsSender instance } => instance,
        { ImplementationFactory: { } factory } => (ISmsSender)factory(sp),
        { ImplementationType: { } type } => (ISmsSender)ActivatorUtilities.CreateInstance(sp, type),
        _ => throw new InvalidOperationException("The registered ISmsSender cannot be constructed for decoration."),
    };

    /// <summary>
    /// Development/Testing-only re-registration of the real <see cref="MockPaymentCaptureSimulator"/> over the
    /// production <see cref="DisabledPaymentCaptureSimulator"/> (refinement-phase-8, 6.4). The <c>bookings/convert</c>
    /// simulator path is a dev/test affordance — production converts via the real b10 webhook confirm, not this
    /// command. Last registration wins, so callers transparently get the succeeding mock in Dev/Testing.
    /// </summary>
    public static IServiceCollection AddDevelopmentPaymentCapture(this IServiceCollection services)
    {
        services.AddSingleton<IPaymentCaptureSimulator, MockPaymentCaptureSimulator>();
        return services;
    }
}
