using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Scripting;
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Scripting;

namespace ZB.MOM.WW.ScadaBridge.InboundAPI;

/// <summary>
/// N1 (arch-review 06 round 2): the Inbound API consumer of the script-artifact
/// invalidation contract (docs/plans/2026-07-08-script-artifact-invalidation-contract.md,
/// handoff item (a)). Subscribes to <see cref="IScriptArtifactChangeBus"/> on host
/// start and, for every <c>ApiMethod</c> notification (e.g. a Transport bundle
/// import overwriting method scripts, published post-commit), drops the compiled
/// handler AND the known-bad record via
/// <see cref="InboundScriptExecutor.InvalidateMethod"/>, so the next request
/// recompiles from the fresh row without waiting for the per-request
/// revision-check self-heal.
/// <para>
/// Fast-path only: the revision check in <c>ExecuteAsync</c> remains the
/// correctness fallback — this bus is in-process/per-node and at-least-once
/// (contract §4), so a missed or duplicate notification must always be harmless.
/// The bus is optional (site roles and plain test hosts register none) — the
/// subscriber then no-ops. SharedScript/Template notifications are ignored: this
/// executor caches only ApiMethod handlers.
/// </para>
/// </summary>
public sealed class ScriptArtifactChangeSubscriber : IHostedService
{
    private readonly InboundScriptExecutor _executor;
    private readonly ApiMethodCache _methodCache;
    private readonly ILogger<ScriptArtifactChangeSubscriber> _logger;
    private readonly IScriptArtifactChangeBus? _bus;
    private IDisposable? _subscription;

    /// <summary>Initializes the subscriber.</summary>
    /// <param name="executor">The compiled-handler cache to invalidate.</param>
    /// <param name="methodCache">
    /// WP2.6b: the short-TTL resolved-<c>ApiMethod</c>-row cache to invalidate alongside
    /// the compiled handler.
    /// </param>
    /// <param name="logger">Logger instance.</param>
    /// <param name="bus">The change bus, or null when the host registers none (site roles, tests).</param>
    public ScriptArtifactChangeSubscriber(
        InboundScriptExecutor executor,
        ApiMethodCache methodCache,
        ILogger<ScriptArtifactChangeSubscriber> logger,
        IScriptArtifactChangeBus? bus = null)
    {
        _executor = executor;
        _methodCache = methodCache;
        _logger = logger;
        _bus = bus;
    }

    /// <inheritdoc />
    public Task StartAsync(CancellationToken cancellationToken)
    {
        if (_bus is null)
        {
            _logger.LogDebug(
                "No IScriptArtifactChangeBus registered; inbound handler-cache freshness relies on the per-request revision check alone.");
            return Task.CompletedTask;
        }

        _subscription = _bus.Subscribe(OnChanged);
        return Task.CompletedTask;
    }

    /// <inheritdoc />
    public Task StopAsync(CancellationToken cancellationToken)
    {
        _subscription?.Dispose();
        _subscription = null;
        return Task.CompletedTask;
    }

    private void OnChanged(ScriptArtifactsChanged notification)
    {
        if (!string.Equals(
                notification.ArtifactKind, ScriptArtifactKinds.ApiMethod, StringComparison.Ordinal))
        {
            return; // only ApiMethod handlers are cached by this executor
        }

        foreach (var name in notification.Names)
        {
            _executor.InvalidateMethod(name);
            _methodCache.Invalidate(name);
        }

        _logger.LogInformation(
            "Invalidated {Count} inbound API method handler(s) after a {Source} script-artifact change.",
            notification.Names.Count, notification.Source);
    }
}
