using System.Windows.Threading;
using DragonISO.App.Services;

namespace DragonISO.App.ViewModels;

// Teams launch / in-call / join-by-URL command helpers â€” split out of
// MainViewModel.cs so the body methods don't live alongside the
// constructor wiring + reactive subscriptions. The four command
// PROPERTIES are declared back in MainViewModel.cs (public API surface);
// this file holds the helpers they invoke.
public sealed partial class MainViewModel
{
    /// <summary>
    /// Wraps a <see cref="TeamsControlBridge"/> invocation in a RelayCommand
    /// that translates the result to a user-visible toast. Centralizes the
    /// toast wording so the four control commands stay consistent.
    /// </summary>
    private RelayCommand MakeTeamsCommand(string label, Func<TeamsControlBridge.InvokeResult> invoke, string successMessage)
    {
        return new RelayCommand(() =>
        {
            switch (invoke())
            {
                case TeamsControlBridge.InvokeResult.Invoked:
                    Toast.Show(successMessage);
                    break;
                case TeamsControlBridge.InvokeResult.TeamsNotRunning:
                    Toast.Warn("Teams isn't running.");
                    break;
                case TeamsControlBridge.InvokeResult.ControlNotFound:
                    Toast.Warn($"{label} control not found â€” are you in a call?");
                    break;
                case TeamsControlBridge.InvokeResult.InvokeFailed:
                    Toast.Warn($"{label} button found but disabled.");
                    break;
            }
        });
    }

    /// <summary>
    /// Body of <c>JoinMeetingCommand</c>. Trims the pasted URL, hands it to
    /// <see cref="TeamsLauncher.TryJoinMeeting"/>, and runs the auto-hide
    /// follow-up if the operator has that preference set.
    /// </summary>
    private void JoinPastedMeeting()
    {
        var url = (_joinMeetingUrl ?? string.Empty).Trim();
        if (string.IsNullOrEmpty(url)) return;
        if (TeamsLauncher.TryJoinMeeting(url, out var error))
        {
            Toast.Show("Joining Teams meetingâ€¦");
            JoinMeetingUrl = string.Empty;
            if (Settings.AutoHideTeamsWindows)
                _ = TeamsLauncher.AutoHideAfterLaunchAsync();
        }
        else
        {
            Toast.Warn($"Could not join: {error}");
        }
    }

    /// <summary>
    /// Pull the meaningful "meeting title" out of Teams' raw window title.
    /// Teams uses formats like:
    ///   "Weekly Standup | Microsoft Teams"
    ///   "Meeting with Alice | Microsoft Teams"
    ///   "Microsoft Teams" (no meeting, just the app)
    /// Strip the trailing " | Microsoft Teams" so the IN-CALL pill stays
    /// short and readable. Truncate beyond 50 chars so a long meeting
    /// subject doesn't push the rest of the IN-CALL bar off screen.
    /// </summary>
    internal static string ExtractMeetingTitle(string windowTitle)
    {
        if (string.IsNullOrWhiteSpace(windowTitle)) return string.Empty;
        var t = windowTitle.Trim();
        foreach (var sep in new[] { " | Microsoft Teams", " | Teams", " - Microsoft Teams", " - Teams" })
        {
            var idx = t.IndexOf(sep, StringComparison.Ordinal);
            if (idx > 0) { t = t.Substring(0, idx).Trim(); break; }
        }
        if (t.Equals("Microsoft Teams", StringComparison.OrdinalIgnoreCase)) return string.Empty;
        if (t.Length > 50) t = t.Substring(0, 47) + "â€¦";
        return t;
    }

    /// <summary>
    /// Meeting-state probe â€” runs on every 1Hz stats tick. We fire the UIA
    /// traversal on a worker thread because it can take 50â€“200ms in a busy
    /// call; the result is marshalled back to the dispatcher to update the
    /// view-model properties. One-tick latency on the displayed state is
    /// preferable to a UI hiccup.
    /// </summary>
    private void PollTeamsMeetingState()
    {
        try
        {
            var teamsRunning = TeamsLauncher.IsRunning();
            if (!teamsRunning)
            {
                TeamsMeetingState = string.Empty;
                IsTeamsInCall = false;
                return;
            }

            _ = Task.Run(() =>
            {
                try
                {
                    // Single UIA traversal returns all three signals (in-call /
                    // muted / camera-off) so we don't pay for three walks of
                    // the same descendant tree at 1Hz.
                    var snap = TeamsControlBridge.DetectCallState();
                    var inCall = snap.IsInCall;
                    var title = inCall ? ExtractMeetingTitle(TeamsLauncher.GetActiveWindowTitle()) : null;
                    _dispatcher.InvokeAsync(() =>
                    {
                        IsTeamsInCall = inCall;
                        TeamsMeetingState = inCall
                            ? (string.IsNullOrEmpty(title) ? "IN CALL" : $"IN CALL Â· {title}")
                            : "READY";
                        IsLocalMuted = inCall && (snap.IsMuted ?? false);
                        IsLocalCameraOff = inCall && (snap.IsCameraOff ?? false);
                    });
                }
                catch { /* UIA flakiness shouldn't crash the stats tick */ }
            });
        }
        catch { /* defensive â€” probe failures must never break the tick */ }
    }
}
