using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.Win32;

namespace ServerMediaTray
{
    internal static class Program
    {
        // === НАСТРОЙКИ ===
        // Где лежит server.js
        private static readonly string BaseDir = @"C:\ServerMedia";
        private static readonly string ServerJs = Path.Combine(BaseDir, "server.js");

        // node.exe: либо из PATH, либо стандартный путь
        private static readonly string NodeExeDefault = @"C:\Program Files\nodejs\node.exe";

        // Логи
        private static readonly string LogsDir = Path.Combine(BaseDir, "logs");

        private const string AppName = "ServerMedia";
        private const string AutorunValueName = "ServerMediaTray";

        [STAThread]
        static void Main()
        {
            try
            {
                Directory.CreateDirectory(LogsDir);

                ApplicationConfiguration.Initialize();
                Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

                Application.ThreadException += (_, e) =>
                    StartupLog("ThreadException: " + e.Exception);

                AppDomain.CurrentDomain.UnhandledException += (_, e) =>
                    StartupLog("UnhandledException: " + (e.ExceptionObject?.ToString() ?? "null"));

                Application.Run(new TrayAppContext());
            }
            catch (Exception ex)
            {
                StartupLog("Fatal: " + ex);
            }
        }

        private static void StartupLog(string msg)
        {
            try
            {
                Directory.CreateDirectory(LogsDir);
                var path = Path.Combine(LogsDir, "tray_startup.log");
                File.AppendAllText(path, $"[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] {msg}\r\n", Encoding.UTF8);
            }
            catch { /* ignore */ }
        }

        private sealed class TrayAppContext : ApplicationContext
        {
            private readonly NotifyIcon _tray;
            private readonly ToolStripMenuItem _miStart;
            private readonly ToolStripMenuItem _miStop;
            private readonly ToolStripMenuItem _miRestart;
            private readonly ToolStripMenuItem _miExit;

            private Process? _nodeProc;

            public TrayAppContext()
            {
                // 1) Автозагрузка приложения вместе с Windows
                EnsureAutorun();

                // 2) Меню
                _miStart = new ToolStripMenuItem("Start", null, (_, __) => StartServer());
                _miStop = new ToolStripMenuItem("Stop", null, (_, __) => StopServer());
                _miRestart = new ToolStripMenuItem("Restart", null, (_, __) => RestartServer());
                _miExit = new ToolStripMenuItem("Exit", null, (_, __) => ExitApp());

                var menu = new ContextMenuStrip();
                menu.Items.AddRange(new ToolStripItem[]
                {
                    _miStart, _miStop, _miRestart,
                    new ToolStripSeparator(),
                    _miExit
                });

                // 3) Иконка из exe (вшитая через ApplicationIcon в csproj)
                var icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath) ?? SystemIcons.Application;

                _tray = new NotifyIcon
                {
                    Text = AppName,
                    Icon = icon,
                    Visible = true,
                    ContextMenuStrip = menu
                };

                // Двойной клик = Restart
                _tray.DoubleClick += (_, __) => RestartServer();

                UpdateMenuState();

                // 4) Автостарт сервера сразу при запуске приложения
                StartServer();
            }

            private static void EnsureAutorun()
            {
                try
                {
                    const string runKeyPath = @"Software\Microsoft\Windows\CurrentVersion\Run";
                    using var key = Registry.CurrentUser.OpenSubKey(runKeyPath, writable: true)
                                  ?? Registry.CurrentUser.CreateSubKey(runKeyPath, writable: true);

                    var exePath = Application.ExecutablePath;
                    var desired = $"\"{exePath}\"";

                    var current = key.GetValue(AutorunValueName) as string;
                    if (!string.Equals(current, desired, StringComparison.OrdinalIgnoreCase))
                        key.SetValue(AutorunValueName, desired);
                }
                catch (Exception ex)
                {
                    StartupLog("EnsureAutorun error: " + ex);
                }
            }

            private void StartServer()
            {
                try
                {
                    if (!File.Exists(ServerJs))
                    {
                        NotifyError($"Не найден server.js: {ServerJs}");
                        return;
                    }

                    if (_nodeProc is { HasExited: false })
                    {
                        NotifyInfo("Сервер уже запущен.");
                        return;
                    }

                    var nodeExe = ResolveNodeExe();
                    if (nodeExe == null)
                    {
                        NotifyError("Не найден node.exe. Добавь Node в PATH или проверь C:\\Program Files\\nodejs\\node.exe");
                        return;
                    }

                    Directory.CreateDirectory(LogsDir);
                    var stamp = DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss");
                    var logPath = Path.Combine(LogsDir, $"server_{stamp}.log");

                    var psi = new ProcessStartInfo
                    {
                        FileName = nodeExe,
                        Arguments = $"\"{ServerJs}\"",
                        WorkingDirectory = BaseDir,
                        UseShellExecute = false,
                        CreateNoWindow = true,
                        RedirectStandardOutput = true,
                        RedirectStandardError = true
                    };

                    var p = new Process { StartInfo = psi, EnableRaisingEvents = true };

                    var logWriter = new StreamWriter(new FileStream(logPath, FileMode.Create, FileAccess.Write, FileShare.ReadWrite), Encoding.UTF8)
                    {
                        AutoFlush = true
                    };

                    p.OutputDataReceived += (_, e) => { if (e.Data != null) logWriter.WriteLine(e.Data); };
                    p.ErrorDataReceived += (_, e) => { if (e.Data != null) logWriter.WriteLine("[ERR] " + e.Data); };

                    p.Exited += (_, __) =>
                    {
                        try { logWriter.WriteLine($"[EXIT] Node exited with code {p.ExitCode}"); } catch { }
                        try { logWriter.Dispose(); } catch { }

                        _nodeProc = null;
                        UpdateMenuState();
                        _tray.ShowBalloonTip(2500, AppName, "Сервер остановился (или упал). См. лог в C:\\ServerMedia\\logs", ToolTipIcon.Warning);
                    };

                    if (!p.Start())
                    {
                        logWriter.Dispose();
                        NotifyError("Не удалось запустить процесс Node.");
                        return;
                    }

                    p.BeginOutputReadLine();
                    p.BeginErrorReadLine();

                    _nodeProc = p;

                    UpdateMenuState();
                    _tray.ShowBalloonTip(1500, AppName, "Сервер запущен (autostart).", ToolTipIcon.Info);
                }
                catch (Exception ex)
                {
                    StartupLog("StartServer error: " + ex);
                    NotifyError("Ошибка запуска сервера. См. tray_startup.log");
                }
            }

            private void StopServer()
            {
                try
                {
                    if (_nodeProc is null || _nodeProc.HasExited)
                    {
                        _nodeProc = null;
                        UpdateMenuState();
                        NotifyInfo("Сервер не запущен.");
                        return;
                    }

                    // Мягко → жёстко
                    try { _nodeProc.CloseMainWindow(); } catch { }
                    if (!_nodeProc.WaitForExit(700))
                    {
                        try { _nodeProc.Kill(true); } catch { }
                    }

                    _nodeProc = null;
                    UpdateMenuState();
                    _tray.ShowBalloonTip(1500, AppName, "Сервер остановлен.", ToolTipIcon.Info);
                }
                catch (Exception ex)
                {
                    StartupLog("StopServer error: " + ex);
                    NotifyError("Ошибка остановки. См. tray_startup.log");
                }
            }

            private void RestartServer()
            {
                StopServer();
                StartServer();
            }

            private void ExitApp()
            {
                StopServer();
                _tray.Visible = false;
                _tray.Dispose();
                Application.Exit();
            }

            private void UpdateMenuState()
            {
                bool running = _nodeProc is { HasExited: false };
                _miStart.Enabled = !running;
                _miStop.Enabled = running;
                _miRestart.Enabled = true;

                _tray.Text = running ? $"{AppName} (running)" : $"{AppName} (stopped)";
            }

            private static string? ResolveNodeExe()
            {
                // 1) Поиск node.exe в PATH
                try
                {
                    var paths = (Environment.GetEnvironmentVariable("PATH") ?? "")
                        .Split(';', StringSplitOptions.RemoveEmptyEntries)
                        .Select(p => p.Trim())
                        .Where(Directory.Exists);

                    foreach (var dir in paths)
                    {
                        var candidate = Path.Combine(dir, "node.exe");
                        if (File.Exists(candidate)) return candidate;
                    }
                }
                catch { }

                // 2) Стандартный путь
                if (File.Exists(NodeExeDefault)) return NodeExeDefault;

                return null;
            }

            private void NotifyInfo(string msg) => _tray.ShowBalloonTip(1500, AppName, msg, ToolTipIcon.Info);
            private void NotifyError(string msg) => _tray.ShowBalloonTip(3000, AppName, msg, ToolTipIcon.Error);
        }
    }
}