using DragonISO.App.Services;

namespace DragonISO.App.ViewModels;

// Bulk operations that touch every (or every-enabled) participant â€”
// Stop all ISOs, Enable all online, Snapshot all enabled frames.
// Split out of MainViewModel.cs so the main file isn't dominated by
// long async iteration loops.
//
// The RecordingCommands partial originally planned at this slot is
// intentionally absent: the recording surface was axed earlier in the
// May 2026 batch (see commit 1d1ce6a). What remains is bulk-state
// manipulation across the participants collection.
public sealed partial class MainViewModel
{
    /// <summary>
    /// Enable ISOs for every online + non-enabled participant in
    /// parallel-ish (sequential await, but each individual EnableIsoAsync
    /// is fast). Tolerates per-participant failures so one bad source
    /// doesn't abort the rest.
    /// </summary>
    private async Task EnableAllOnlineAsync()
    {
        var candidates = Participants.Where(p => p.IsOnline && !p.IsEnabled).ToArray();
        var enabled = 0;
        foreach (var p in candidates)
        {
            try
            {
                var resolvedName = string.IsNullOrWhiteSpace(p.CustomName)
                    ? OutputNameTemplate.Render(
                        OutputNameTemplate.Get(),
                        p.Id,
                        p.DisplayName)
                    : p.CustomName;
                // 3-arg overload (no recordOverride) â€” recording surface axed,
                // so the engine's per-pipeline recorder sink stays unattached.
                await _controller.EnableIsoAsync(p.Id, resolvedName, CancellationToken.None);
                p.IsEnabled = true;
                enabled++;
            }
            catch
            {
                // Per-participant best-effort â€” one bad source shouldn't
                // abort the bulk operation.
            }
        }
        Toast.Show(enabled == 0
            ? "No participants to enable"
            : $"Enabled {enabled} ISO(s)");
    }

    /// <summary>
    /// Emergency-stop: disable every running ISO. Confirmation dialog with
    /// default-No guards mid-show misclicks; the regret cost of yanking 5
    /// ISOs is far higher than the Enter-press cost of the prompt.
    /// </summary>
    private async Task StopAllIsosAsync()
    {
        // Snapshot first so the collection doesn't mutate while we iterate.
        var enabled = Participants.Where(p => p.IsEnabled).ToArray();
        if (enabled.Length == 0)
        {
            Toast.Show("No ISOs to stop");
            return;
        }
        var confirm = System.Windows.MessageBox.Show(
            $"Stop {enabled.Length} running ISO(s)?\n\nThis tears down every active pipeline immediately.",
            "Dragon-ISO â€” Stop all ISOs",
            System.Windows.MessageBoxButton.YesNo,
            System.Windows.MessageBoxImage.Warning,
            System.Windows.MessageBoxResult.No);
        if (confirm != System.Windows.MessageBoxResult.Yes) return;

        foreach (var p in enabled)
        {
            try { await _controller.DisableIsoAsync(p.Id, CancellationToken.None); }
            catch { /* defensive */ }
            p.IsEnabled = false;
        }
        Toast.Show($"Stopped {enabled.Length} ISO(s)");
    }

    /// <summary>
    /// Save a PNG of every currently-enabled participant's latest
    /// processed frame to a timestamped subdirectory under
    /// %USERPROFILE%\Pictures\Dragon-ISO\. One folder per Snapshot All click
    /// so back-to-back clicks don't comingle. Useful for end-of-meeting
    /// archives, recapping who showed up, etc.
    /// </summary>
    private void SnapshotAll()
    {
        var enabled = Participants.Where(p => p.IsEnabled).ToArray();
        if (enabled.Length == 0)
        {
            Toast.Warn("No enabled participants to snapshot");
            return;
        }

        var rootDir = System.IO.Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.MyPictures),
            "Dragon-ISO",
            $"snapshots-{DateTimeOffset.Now:yyyyMMdd_HHmmss}");

        try
        {
            System.IO.Directory.CreateDirectory(rootDir);
        }
        catch (Exception ex)
        {
            Toast.Warn($"Couldn't create snapshot dir: {ex.Message}");
            return;
        }

        var saved = 0;
        var failed = 0;
        foreach (var p in enabled)
        {
            try
            {
                var frame = _controller.GetLatestProcessedFrame(p.Id);
                if (frame is null || frame.Pixels.IsEmpty) { failed++; continue; }

                var safeName = string.Join("_", p.DisplayName.Split(System.IO.Path.GetInvalidFileNameChars()));
                if (string.IsNullOrWhiteSpace(safeName)) safeName = "participant";
                var path = System.IO.Path.Combine(rootDir, $"{safeName}.png");

                var stride = frame.Width * 4;
                var bmp = new System.Windows.Media.Imaging.WriteableBitmap(
                    frame.Width, frame.Height, 96, 96,
                    System.Windows.Media.PixelFormats.Bgra32, null);
                bmp.WritePixels(
                    new System.Windows.Int32Rect(0, 0, frame.Width, frame.Height),
                    frame.Pixels.ToArray(), stride, 0);

                using var fs = new System.IO.FileStream(path, System.IO.FileMode.Create);
                var encoder = new System.Windows.Media.Imaging.PngBitmapEncoder();
                encoder.Frames.Add(System.Windows.Media.Imaging.BitmapFrame.Create(bmp));
                encoder.Save(fs);
                saved++;
            }
            catch { failed++; }
        }

        Toast.Show(failed > 0
            ? $"Saved {saved} snapshot(s) ({failed} failed) to Pictures\\Dragon-ISO\\{System.IO.Path.GetFileName(rootDir)}"
            : $"Saved {saved} snapshot(s) to Pictures\\Dragon-ISO\\{System.IO.Path.GetFileName(rootDir)}");
    }
}
