using TeamsISO.Engine.Domain;

namespace TeamsISO.Engine.Controller;

/// <summary>
/// Top-of-engine API the WPF host (Phase C) and any future control APIs (OSC / WebSocket in v2.0)
/// bind to. All commands are async and cancellable.
/// </summary>
public interface IIsoController : IAsyncDisposable
{
    /// <summary>Observable list of currently-known meeting participants.</summary>
    IObservable<IReadOnlyList<Participant>> Participants { get; }

    /// <summary>Observable stream of engine alerts (for UI banner display and ops logging).</summary>
    IObservable<EngineAlert> Alerts { get; }

    /// <summary>Current global processing settings.</summary>
    FrameProcessingSettings GlobalSettings { get; }

    /// <summary>Current NDI group settings (discovery + output groups).</summary>
    NdiGroupSettings GroupSettings { get; }

    /// <summary>Starts discovery and supervises the runtime probe. Returns once startup is complete.</summary>
    Task StartAsync(CancellationToken cancellationToken);

    /// <summary>Returns the latest <see cref="IsoHealthStats"/> for a given participant's ISO, or empty if none.</summary>
    IsoHealthStats GetStats(Guid participantId);

    /// <summary>
    /// Returns the most recent processed frame for the given participant's pipeline,
    /// or null if no pipeline is running or no frame has been processed yet. Used by
    /// the WPF host to render in-app preview thumbnails. The returned <see cref="Pipeline.ProcessedFrame"/>
    /// is immutable; callers may copy / scale freely.
    /// </summary>
    Pipeline.ProcessedFrame? GetLatestProcessedFrame(Guid participantId);

    /// <summary>Enables an ISO pipeline for the given participant.</summary>
    Task EnableIsoAsync(Guid participantId, string? customName, CancellationToken cancellationToken);

    /// <summary>
    /// Enable an ISO pipeline with a per-call override for whether this specific
    /// pipeline gets a recorder attached. <paramref name="recordOverride"/> null
    /// means "follow the global <see cref="RecordingEnabled"/> flag" (the
    /// default behavior); true forces a recorder; false forces no recorder.
    /// Used by the UI to give the operator per-participant recording opt-out.
    /// </summary>
    Task EnableIsoAsync(Guid participantId, string? customName, bool? recordOverride, CancellationToken cancellationToken);

    /// <summary>Disables and tears down the pipeline for the given participant.</summary>
    Task DisableIsoAsync(Guid participantId, CancellationToken cancellationToken);

    /// <summary>Updates global processing settings and persists them. Currently does not restart running pipelines (Phase C wires that).</summary>
    Task SetGlobalSettingsAsync(FrameProcessingSettings settings, CancellationToken cancellationToken);

    /// <summary>
    /// Updates the NDI group configuration and persists it. Group changes apply on next process
    /// restart — rebuilding finder/sender handles mid-flight would orphan running pipelines.
    /// </summary>
    Task SetGroupSettingsAsync(NdiGroupSettings groupSettings, CancellationToken cancellationToken);

    /// <summary>
    /// Forces NDI discovery to rebuild its finder on the next poll tick, then re-emits all
    /// currently-visible sources as freshly-added. Useful right after applying a new
    /// transcoder topology when Teams or other senders need to be re-detected on the new
    /// group, without having to restart the whole process.
    /// </summary>
    void RefreshDiscovery();

    /// <summary>
    /// Per-output recording on/off. When enabled, each subsequently-started ISO writes
    /// its normalized output to <paramref name="outputDirectory"/>/&lt;display-name&gt;/.
    /// Already-running ISOs are not retroactively recorded (would require restarting
    /// their pipelines, which can hiccup live output) — the operator should disable +
    /// re-enable a participant to start recording it.
    /// </summary>
    void SetRecording(bool enabled, string? outputDirectory);

    /// <summary>True if <see cref="SetRecording"/> has been called with enabled=true.</summary>
    bool RecordingEnabled { get; }

    /// <summary>The output directory configured by the most recent <see cref="SetRecording"/> call.</summary>
    string? RecordingDirectory { get; }

    /// <summary>
    /// Drop a timestamped marker into every currently-recording pipeline. No-op
    /// if no pipelines are recording. Markers land in each recording's
    /// manifest.json under the <c>markers[]</c> array.
    /// </summary>
    void AddRecordingMarker(string label);
}
