using System.Runtime.Versioning;
using RioJoy.Core.Mapping;
using RioJoy.Core.Profiles;
using RioJoy.Tray.Editor;
using RioJoy.Tray.Os;

namespace RioJoy.Tray;

/// <summary>
/// Owns the tray icon, menu, and the RIOJoy runtime. The menu mirrors the legacy
/// console menu (axis resets, version/status, diagnostic toggles, quit) and adds
/// profile selection (auto vs. manual) and a "start with Windows" toggle. The
/// auto-switch watcher is polled on a UI timer so menu/status updates stay on the
/// UI thread.
/// </summary>
[SupportedOSPlatform("windows")]
internal sealed class TrayApplicationContext : ApplicationContext
{
    private static readonly string ConfigPath =
        Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "RIOJoy", "config.json");

    private static readonly TimeSpan PollInterval = TimeSpan.FromSeconds(1);

    private readonly AppConfig _config;
    private readonly RioCoordinator _coordinator;
    private readonly AutoSwitchWatcher _watcher;
    private readonly System.Windows.Forms.Timer _pollTimer;
    private readonly NotifyIcon _trayIcon;
    private readonly ToolStripMenuItem _statusItem;

    public TrayApplicationContext()
    {
        _config = ConfigStore.Load(ConfigPath);

        _coordinator = new RioCoordinator(() => _config);
        _coordinator.StatusChanged += _ => RefreshOnUiThread();

        _watcher = new AutoSwitchWatcher(new ForegroundProcessProvider(), () => _config);
        _watcher.DecisionChanged += d => _coordinator.ApplyDecision(d);

        _statusItem = new ToolStripMenuItem("Status: starting…") { Enabled = false };

        _trayIcon = new NotifyIcon
        {
            Icon = SystemIcons.Application,
            Text = "RIOJoy",
            Visible = true,
            ContextMenuStrip = BuildMenu(),
        };

        // Poll the foreground app on the UI thread.
        _pollTimer = new System.Windows.Forms.Timer { Interval = (int)PollInterval.TotalMilliseconds };
        _pollTimer.Tick += (_, _) => _watcher.Poll();
        _pollTimer.Start();
        _watcher.Poll();
    }

    private ContextMenuStrip BuildMenu()
    {
        var menu = new ContextMenuStrip();

        menu.Items.Add(_statusItem);
        menu.Items.Add(new ToolStripSeparator());

        // Profile selection: Auto + each profile + manual dormant.
        var profileMenu = new ToolStripMenuItem("Profile");
        profileMenu.DropDownOpening += (_, _) => RebuildProfileMenu(profileMenu);
        RebuildProfileMenu(profileMenu);
        menu.Items.Add(profileMenu);

        menu.Items.Add("Edit profile…", null, (_, _) => OpenEditor());

        // RIO commands mirroring the legacy console menu.
        var commands = new ToolStripMenuItem("RIO commands");
        AddCommand(commands, "Reset all analog axes", RioCommandCode.ResetAllAxes);
        AddCommand(commands, "Reset throttle", RioCommandCode.ResetThrottle);
        AddCommand(commands, "Reset left pedal", RioCommandCode.ResetLeftPedal);
        AddCommand(commands, "Reset right pedal", RioCommandCode.ResetRightPedal);
        AddCommand(commands, "Reset vertical joystick", RioCommandCode.ResetVerticalJoystick);
        AddCommand(commands, "Reset horizontal joystick", RioCommandCode.ResetHorizontalJoystick);
        commands.DropDownItems.Add(new ToolStripSeparator());
        AddCommand(commands, "Reset digital (general)", RioCommandCode.GeneralReset);
        AddCommand(commands, "Request version && status", RioCommandCode.RequestVersionAndCheck);
        AddCommand(commands, "Toggle raw-axes readout", RioCommandCode.ToggleRawAxesReadout);
        AddCommand(commands, "Toggle poll-rate readout", RioCommandCode.TogglePollRateReadout);
        menu.Items.Add(commands);

        menu.Items.Add(new ToolStripSeparator());

        var autoStart = new ToolStripMenuItem("Start with Windows")
        {
            CheckOnClick = true,
            Checked = AutoStartManager.IsEnabled(),
        };
        autoStart.Click += (_, _) => AutoStartManager.SetEnabled(autoStart.Checked);
        menu.Items.Add(autoStart);

        menu.Items.Add("Exit", null, (_, _) => Quit());

        return menu;
    }

    private void RebuildProfileMenu(ToolStripMenuItem profileMenu)
    {
        profileMenu.DropDownItems.Clear();

        var auto = new ToolStripMenuItem("Auto (foreground app)") { Checked = _coordinator.IsAuto };
        auto.Click += (_, _) => _coordinator.SetAuto();
        profileMenu.DropDownItems.Add(auto);
        profileMenu.DropDownItems.Add(new ToolStripSeparator());

        foreach (RioProfile profile in _config.Profiles)
        {
            RioProfile captured = profile;
            var item = new ToolStripMenuItem(profile.Name)
            {
                Checked = !_coordinator.IsAuto && _coordinator.ActiveProfileName == profile.Name,
            };
            item.Click += (_, _) => _coordinator.SetManualProfile(captured);
            profileMenu.DropDownItems.Add(item);
        }

        if (_config.Profiles.Count == 0)
            profileMenu.DropDownItems.Add(new ToolStripMenuItem("(no profiles configured)") { Enabled = false });

        profileMenu.DropDownItems.Add(new ToolStripSeparator());
        var dormant = new ToolStripMenuItem("Dormant (release port)")
        {
            Checked = !_coordinator.IsAuto && _coordinator.ActiveProfileName is null,
        };
        dormant.Click += (_, _) => _coordinator.SetManualDormant();
        profileMenu.DropDownItems.Add(dormant);
    }

    private void OpenEditor()
    {
        // The cockpit panel layout is built in (CockpitPanel); the editor just needs
        // a profile to edit.
        RioProfile profile = _config.Profiles.FirstOrDefault() ?? AddBlankProfile();

        var editor = new ProfileEditorForm(profile);
        editor.Saved += _ => ConfigStore.Save(_config, ConfigPath);
        editor.Show();
    }

    private RioProfile AddBlankProfile()
    {
        var profile = new RioProfile { Name = "New profile" };
        _config.Profiles.Add(profile);
        return profile;
    }

    private void AddCommand(ToolStripMenuItem parent, string text, RioCommandCode code)
    {
        var item = new ToolStripMenuItem(text);
        item.Click += (_, _) => _coordinator.Runtime?.Trigger(code);
        parent.DropDownItems.Add(item);
    }

    private void RefreshOnUiThread()
    {
        if (_trayIcon.ContextMenuStrip?.InvokeRequired == true)
        {
            _trayIcon.ContextMenuStrip.BeginInvoke(RefreshStatus);
            return;
        }

        RefreshStatus();
    }

    private void RefreshStatus()
    {
        _statusItem.Text = $"Status: {_coordinator.Status}";
        _trayIcon.Text = $"RIOJoy — {_coordinator.Status}".Length <= 63
            ? $"RIOJoy — {_coordinator.Status}"
            : "RIOJoy";
    }

    private void Quit()
    {
        _pollTimer.Stop();
        _coordinator.Dispose();
        _trayIcon.Visible = false;
        ExitThread();
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _pollTimer.Dispose();
            _coordinator.Dispose();
            _trayIcon.Dispose();
        }

        base.Dispose(disposing);
    }
}
