using VoiceCat.Interop;

namespace VoiceCat.App.Forms;

/// <summary>
/// Per-remote-user, per-stream gain/mute/noise-reduction settings (docs/voice.md §1, §10).
/// One row is rendered for each stream the remote user is currently publishing. Each row
/// controls only that stream — so a listener can turn down one user's desktop audio while
/// keeping their mic, and independently noise-reduce a third user. All of these are
/// listener-local: no protocol traffic, no effect on other listeners.
///
/// Changes apply in real time as the user adjusts controls (no OK/Cancel round-trip); the
/// Close button just dismisses. State lives in the core and is not persisted across sessions.
/// The dialog is a modal snapshot of the streams active when it was opened — close and
/// reopen to see streams started/stopped in the meantime.
/// </summary>
public sealed class PerUserTuningDialog : Form
{
    private readonly VoiceCatClient _client;
    private readonly uint _userId;

    private readonly List<StreamRow> _rows = new();

    public PerUserTuningDialog(VoiceCatClient client, uint userId, string nickname)
    {
        _client = client;
        _userId = userId;

        AutoScaleMode = AutoScaleMode.Font;
        FormBorderStyle = FormBorderStyle.FixedDialog;
        MaximizeBox = false;
        MinimizeBox = false;
        StartPosition = FormStartPosition.CenterParent;
        Text = $"User settings — {nickname}";

        BuildControls(nickname);
    }

    private void BuildControls(string nickname)
    {
        var lblTitle = new Label
        {
            Text = $"Settings for {nickname}",
            AutoSize = true,
            Font = new Font(Font, FontStyle.Bold),
            Location = new Point(12, 12),
            TabIndex = 0,
        };

        var streams = _client.ListUserStreams(_userId);

        const int rowH = 70;     // label + trackbar + mute/nr
        const int rowGap = 8;
        int y = 44;

        if (streams.Count == 0)
        {
            var lblEmpty = new Label
            {
                Text = "No active streams.",
                AutoSize = true,
                Location = new Point(12, y),
            };
            Controls.Add(lblTitle);
            Controls.Add(lblEmpty);
            ClientSize = new Size(420, 44 + lblEmpty.PreferredHeight + 16);
            return;
        }

        Controls.Add(lblTitle);

        int tabIndex = 1;
        foreach (var s in streams)
        {
            var row = CreateRow(s, y, ref tabIndex);
            _rows.Add(row);
            Controls.AddRange(row.Controls);
            y += rowH + rowGap;
        }

        var btnClose = new Button
        {
            Text = "&Close",
            DialogResult = DialogResult.OK,
            Location = new Point(336, y + 4),
            Size = new Size(75, 27),
            TabIndex = tabIndex,
        };
        AcceptButton = btnClose;

        Controls.Add(btnClose);

        ClientSize = new Size(420, y + 40);
    }

    private StreamRow CreateRow(StreamSummary s, int y, ref int tabIndex)
    {
        // Read back the listener's current state for this stream (defaults if never set).
        float gain0 = 1.0f; bool mute0 = false; bool nr0 = false;
        var (r, state) = _client.GetRemoteStream(_userId, s.StreamId);
        if (r == VcResult.Ok && state is not null)
        {
            gain0 = state.Gain;
            mute0 = state.Muted;
            nr0   = state.NoiseReduction;
        }

        var lblName = new Label
        {
            Text = StreamLabel(s),
            AutoSize = true,
            Font = new Font(Font, FontStyle.Bold),
            Location = new Point(12, y),
            TabIndex = tabIndex++,
        };

        var trkGain = new TrackBar
        {
            AccessibleName = $"Gain for {s.Label}",
            AccessibleDescription = "Volume level for this stream. 100 is normal (1.0×); range 0–400 percent.",
            Location = new Point(12, y + 20),
            Size = new Size(260, 45),
            Minimum = 0,
            Maximum = 400,
            Value = ClampToTrack(gain0),
            TickFrequency = 25,
            SmallChange = 5,
            LargeChange = 25,
            TabIndex = tabIndex++,
        };

        var lblGainValue = new Label
        {
            AutoSize = true,
            Location = new Point(280, y + 28),
            TabIndex = tabIndex++,
        };
        void UpdateGainLabel() =>
            lblGainValue.Text = $"{trkGain.Value}% ({trkGain.Value / 100f:F1}×)";
        UpdateGainLabel();

        var chkMute = new CheckBox
        {
            Text = "&Mute",
            AutoSize = true,
            Location = new Point(12, y + 48),
            Checked = mute0,
            TabIndex = tabIndex++,
        };

        var chkNr = new CheckBox
        {
            Text = "&Noise reduction",
            AutoSize = true,
            Location = new Point(96, y + 48),
            Checked = nr0,
            TabIndex = tabIndex++,
        };

        var row = new StreamRow(s.StreamId, trkGain, chkMute, chkNr,
                                new Control[] { lblName, trkGain, lblGainValue, chkMute, chkNr });

        trkGain.Scroll += (_, _) =>
        {
            UpdateGainLabel();
            row.Apply(_client, _userId);
        };
        chkMute.CheckedChanged += (_, _) => row.Apply(_client, _userId);
        chkNr.CheckedChanged   += (_, _) => row.Apply(_client, _userId);

        return row;
    }

    private static string StreamLabel(StreamSummary s)
    {
        var kind = s.Kind switch
        {
            VcStreamKind.Mic         => "Mic",
            VcStreamKind.ScreenAudio => "Screen audio",
            VcStreamKind.AuxDevice   => "Aux device",
            _                        => "Stream",
        };
        return string.IsNullOrWhiteSpace(s.Label) ? kind : $"{kind} — {s.Label}";
    }

    private static int ClampToTrack(float gain)
    {
        int v = (int)Math.Round(gain * 100f);
        return Math.Max(0, Math.Min(400, v));
    }

    private sealed class StreamRow
    {
        private readonly uint _streamId;
        private readonly TrackBar _trkGain;
        private readonly CheckBox _chkMute;
        private readonly CheckBox _chkNr;
        public Control[] Controls { get; }

        public StreamRow(uint streamId, TrackBar trkGain, CheckBox chkMute, CheckBox chkNr, Control[] controls)
        {
            _streamId = streamId;
            _trkGain = trkGain;
            _chkMute = chkMute;
            _chkNr = chkNr;
            Controls = controls;
        }

        public void Apply(VoiceCatClient client, uint userId)
        {
            float gain = _trkGain.Value / 100f;
            client.SetRemoteStream(userId, _streamId, gain, _chkMute.Checked, _chkNr.Checked);
        }
    }
}
