using Microsoft.AspNetCore.Components.Authorization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using ZB.MOM.WW.ScadaBridge.CentralUI.Auth;
using ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared;
using ZB.MOM.WW.ScadaBridge.CentralUI.ScriptAnalysis;
using ZB.MOM.WW.ScadaBridge.CentralUI.Services;
using ZB.MOM.WW.ScadaBridge.HealthMonitoring;
using ZB.MOM.WW.ScadaBridge.KpiHistory;

namespace ZB.MOM.WW.ScadaBridge.CentralUI;

public static class ServiceCollectionExtensions
{
    /// <summary>
    /// Registers all Central UI services including Blazor, auth state, dialogs, audit query, and script analysis.
    /// </summary>
    /// <param name="services">The service collection to configure.</param>
    /// <returns>The <paramref name="services"/> instance for chaining.</returns>
    public static IServiceCollection AddCentralUI(this IServiceCollection services)
    {
        services.AddRazorComponents()
            .AddInteractiveServerComponents();

        services.AddHttpContextAccessor();

        services.AddScoped<AuthenticationStateProvider, CookieAuthenticationStateProvider>();
        services.AddCascadingAuthenticationState();

        // Resolves the current user's permitted site set from their SiteId claims
        // so Deployment/Monitoring pages can enforce site scoping.
        services.AddScoped<SiteScopeService>();

        // Centralised dialog service: pages inject IDialogService and a single
        // <DialogHost /> in MainLayout renders the active dialog. See
        // Components/Shared/IDialogService.cs.
        services.AddScoped<IDialogService, DialogService>();

        // CentralUI facade over IAuditLogRepository so the
        // results grid can be tested with a stubbed query source.
        //
        // Registered with an explicit factory so the IServiceScopeFactory ctor is
        // always chosen — AuditLogQueryService has a second (test-seam) ctor that
        // takes IAuditLogRepository directly, and both are constructor-resolvable,
        // so default activation would be ambiguous. The scope-factory ctor opens a
        // fresh DbContext per query, which is what keeps the page's auto-load from
        // racing AuditFilterBar's site enumeration on the shared scoped context.
        services.AddScoped<IAuditLogQueryService>(sp => new AuditLogQueryService(
            sp.GetRequiredService<IServiceScopeFactory>(),
            sp.GetRequiredService<ICentralHealthAggregator>()));

        // Server-side streaming CSV export.
        // Backs the Audit Log page's Export button via GET /api/centralui/audit/export.
        services.AddScoped<IAuditLogExportService, AuditLogExportService>();

        // CentralUI facade over IKpiHistoryRepository that
        // fetches a raw series and reduces it with KpiSeriesBucketer for the trend chart.
        //
        // Registered with an explicit factory so the IServiceScopeFactory ctor is
        // always chosen — KpiHistoryQueryService has a second (test-seam) ctor that
        // takes IKpiHistoryRepository directly, and both are constructor-resolvable,
        // so default activation would be ambiguous. The scope-factory ctor opens a
        // fresh DbContext per query, mirroring AuditLogQueryService so a chart's
        // auto-load never races other reads on the shared circuit-scoped context.
        services.AddScoped<IKpiHistoryQueryService>(sp => new KpiHistoryQueryService(
            sp.GetRequiredService<IServiceScopeFactory>(),
            sp.GetRequiredService<IOptions<KpiHistoryOptions>>()));

        // OPC UA Tag Browser: facade over CommunicationService.BrowseNodeAsync
        // that enforces the CentralUI-side Design-role trust boundary and translates
        // transport failures into typed BrowseFailure results for the dialog.
        services.AddScoped<IBrowseService, BrowseService>();

        // Verify Endpoint: facade over CommunicationService.VerifyEndpointAsync
        // that enforces the same CentralUI-side Design-role trust boundary as the browse
        // service, serializes the in-progress endpoint config, and translates transport
        // failures into typed VerifyEndpointResults. Backs the "Verify endpoint" button
        // on the OPC UA endpoint editor (read-only connect probe, never trusts certs).
        services.AddScoped<IEndpointVerificationService, EndpointVerificationService>();

        // OPC UA Cert Management: facade over the three
        // CommunicationService cert-trust relay methods. Enforces the CentralUI-side
        // role trust boundary (Trust + Remove require Administrator, List requires
        // Designer) and translates transport failures into typed CertTrustResults.
        // Backs the "Trust certificate" button on the OPC UA endpoint editor and the
        // connection-certificates management page (node-wide site PKI store).
        services.AddScoped<ICertManagementService, CertManagementService>();

        // Test Bindings: facade over CommunicationService.ReadTagValuesAsync —
        // same Design-role guard + typed-failure translation as the browse
        // service. Backs the Test Bindings dialog on the Configure Instance
        // page (one-shot live read of every bound attribute, grouped by
        // connection).
        services.AddScoped<IBindingTester, BindingTester>();

        // Operator Alarm Summary: read-only page that aggregates the
        // current alarms across a site's Enabled instances. The service fans out
        // one debug snapshot per instance via IInstanceSnapshotClient — a thin
        // facade over CommunicationService.RequestDebugSnapshotAsync (the same
        // single-shot Ask the Debug View uses) — and flattens the alarm states.
        services.AddScoped<IInstanceSnapshotClient, CommunicationInstanceSnapshotClient>();
        // The fan-out itself stays scoped (it holds scoped repositories + the snapshot
        // client), but the page resolves it through SharedAlarmSummaryService — a process
        // singleton that memoizes the fan-out per site with single-flight, so N circuits
        // watching one site cost ONE fan-out per window instead of N (arch-review WP2.4).
        services.AddScoped<AlarmSummaryService>();
        services.AddSingleton<IAlarmSummaryService, SharedAlarmSummaryService>();

        // Process-level memoized point-in-time KPI snapshots (arch-review WP2.4, finding
        // #8's per-circuit half). Singleton on purpose: the memo has to outlive any one
        // circuit for N dashboards to share a single SQL round per refresh window.
        services.AddSingleton<IKpiSnapshotCache, KpiSnapshotCache>();

        // Secured Writes: dispatches the two-person secured-write commands
        // (submit / approve / reject / list) to the central ManagementActor through the
        // in-process ManagementActorHolder seam — the same Ask path the HTTP /management
        // endpoint uses. The server stays the single enforcer of role gating,
        // separation-of-duties (no self-approval), the MxGateway device relay on approve,
        // and the append-only audit trail; the page only SUBMITS commands.
        services.AddScoped<ISecuredWriteService, SecuredWriteService>();

        // Connection live-status: projects the per-site health reports'
        // name-keyed connection statuses onto a connection-id → ConnectionHealth map
        // so the design DataConnections page can render a live badge per connection
        // node. Reuses the existing site→central health transport via
        // ICentralHealthAggregator — no new plumbing.
        services.AddScoped<IConnectionHealthQueryService, ConnectionHealthQueryService>();

        // Move-data-connection: dispatches MoveDataConnectionCommand to the
        // central ManagementActor through the in-process ManagementActorHolder seam —
        // the same Ask path the HTTP /management endpoint uses. The server stays the
        // single enforcer of the Designer gate and every move guard (target exists, no
        // name collision, no instance binding, no native-alarm-source name reference);
        // the move dialog only SUBMITS the command and renders the returned outcome.
        services.AddScoped<IDataConnectionMoveService, DataConnectionMoveService>();

        // Schema Library: authoring + read accessors for the reusable named
        // JSON-Schema library (SharedSchema + ISharedSchemaRepository).
        //
        // ISchemaLibraryService dispatches the CRUD commands to the central
        // ManagementActor through the in-process ManagementActorHolder seam — the same
        // Ask path the HTTP /management endpoint uses — so the server stays the single
        // enforcer of the Designer gate, the unique-name guard, and JSON-Schema
        // validation; the page only SUBMITS commands.
        services.AddScoped<ISchemaLibraryService, SchemaLibraryService>();

        // ISchemaLibraryQueryService is the READ counterpart that the schema-driven
        // value-entry forms reuse to resolve {"$ref":"lib:Name"} pointers — a name →
        // schema-JSON map backed by ISharedSchemaRepository over a fresh DI scope per
        // query (mirroring the AuditLog / KPI query services, off the circuit-scoped
        // DbContext). Read-only; no mutation goes through it.
        services.AddScoped<ISchemaLibraryQueryService, SchemaLibraryQueryService>();

        // Template inheritance preview: a read-only facade that dispatches
        // GetResolvedTemplateMembersCommand to the central ManagementActor through the
        // in-process ManagementActorHolder seam (same Ask path as the HTTP /management
        // endpoint). Powers the template editor's FULL transitively-inherited member set
        // + base-changed staleness banner; never mutates rows, not on the deploy path.
        services.AddScoped<ITemplateInheritanceQueryService, TemplateInheritanceQueryService>();

        // SMS Notifications: the NotificationListForm Type selector offers only the
        // notification channels that actually have a registered delivery adapter. The
        // catalog projects the registered INotificationDeliveryAdapter set (Email + SMS,
        // registered scoped by AddNotificationOutbox into this same container) to its
        // distinct NotificationType set — no hardcoded {Email, Sms}. Scoped because the
        // adapters it enumerates are scoped (they hold a scoped INotificationRepository).
        services.AddScoped<INotificationChannelCatalog, NotificationChannelCatalog>();

        // Roslyn-backed C# analysis for the Monaco script editor.
        // Scoped because SharedScriptCatalog wraps a scoped service.
        services.AddMemoryCache(o => o.SizeLimit = 200);
        services.AddScoped<ISharedScriptCatalog, SharedScriptCatalog>();
        services.AddScoped<ScriptAnalysisService>();

        return services;
    }
}
