using System.Diagnostics;

namespace Dada.Supervisor;

internal sealed class SupervisorForm : Form
{
    private readonly NotifyIcon trayIcon;
    private readonly Label statusTitle;
    private readonly Label statusDetail;
    private readonly Label apiStatus;
    private readonly Label workerStatus;
    private readonly Label storageStatus;
    private readonly FlowLayoutPanel actions;
    private readonly ToolStripMenuItem openMenuItem;
    private readonly ToolStripMenuItem diagnosticsMenuItem;
    private readonly ToolStripMenuItem restartMenuItem;
    private readonly string correlationId = $"SUP-{Guid.NewGuid():N}"[..12].ToUpperInvariant();
    private SupervisorState state;

    internal SupervisorForm(SupervisorState initialState = SupervisorState.Starting)
    {
        state = initialState;
        AutoScaleMode = AutoScaleMode.Dpi;
        BackColor = Color.FromArgb(246, 247, 249);
        ClientSize = new Size(480, 480);
        Font = new Font("Segoe UI", 9F);
        FormBorderStyle = FormBorderStyle.FixedDialog;
        MaximizeBox = false;
        StartPosition = FormStartPosition.CenterScreen;
        Text = "Dada";

        var header = new Panel { BackColor = Color.FromArgb(24, 28, 34), Dock = DockStyle.Top, Height = 126, Padding = new Padding(28, 20, 28, 16) };
        var brand = new Label { AutoSize = true, Font = new Font("Segoe UI Semibold", 12F), ForeColor = Color.White, Location = new Point(28, 18), Text = "DADA" };
        statusTitle = new Label { AutoEllipsis = true, Font = new Font("Microsoft YaHei UI", 15F, FontStyle.Bold), ForeColor = Color.White, Location = new Point(28, 49), Size = new Size(420, 32) };
        statusDetail = new Label { Font = new Font("Microsoft YaHei UI", 9F), ForeColor = Color.FromArgb(181, 188, 198), Location = new Point(28, 82), Size = new Size(420, 38) };
        header.Controls.AddRange([brand, statusTitle, statusDetail]);

        var componentPanel = new TableLayoutPanel
        {
            BackColor = Color.White,
            ColumnCount = 2,
            Dock = DockStyle.Top,
            Height = 172,
            Padding = new Padding(28, 20, 28, 12),
            RowCount = 3,
        };
        componentPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 62));
        componentPanel.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 38));
        componentPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 33));
        componentPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 33));
        componentPanel.RowStyles.Add(new RowStyle(SizeType.Percent, 34));
        apiStatus = AddStatusRow(componentPanel, 0, "本机 API", ApiStatus(initialState));
        workerStatus = AddStatusRow(componentPanel, 1, "后台 Worker", WorkerStatus(initialState));
        storageStatus = AddStatusRow(componentPanel, 2, "本地数据", StorageStatus(initialState));

        actions = new FlowLayoutPanel
        {
            BackColor = Color.FromArgb(246, 247, 249),
            Dock = DockStyle.Fill,
            FlowDirection = FlowDirection.LeftToRight,
            Padding = new Padding(24, 24, 20, 16),
            WrapContents = true,
        };

        Controls.Add(actions);
        Controls.Add(componentPanel);
        Controls.Add(header);

        var menu = new ContextMenuStrip();
        openMenuItem = new ToolStripMenuItem("打开 Dada", null, (_, _) => OpenProduct());
        menu.Items.Add(openMenuItem);
        menu.Items.Add("运行状态", null, (_, _) => RestoreWindow());
        diagnosticsMenuItem = new ToolStripMenuItem("打开诊断", null, (_, _) => ShowDiagnostics());
        menu.Items.Add(diagnosticsMenuItem);
        restartMenuItem = new ToolStripMenuItem("重新启动服务", null, (_, _) =>
        {
            SetState(SupervisorState.Starting);
            RestartRequested?.Invoke();
        });
        menu.Items.Add(restartMenuItem);
        menu.Items.Add(new ToolStripSeparator());
        menu.Items.Add("退出 Dada", null, (_, _) => Close());
        trayIcon = new NotifyIcon
        {
            ContextMenuStrip = menu,
            Icon = SystemIcons.Application,
            Text = TrayText(initialState),
            Visible = !SystemInformation.UserInteractive ? false : true,
        };
        trayIcon.DoubleClick += (_, _) => RestoreWindow();

        FormClosing += (_, _) => trayIcon.Visible = false;
        Resize += (_, _) =>
        {
            if (WindowState == FormWindowState.Minimized) Hide();
        };
        SetState(initialState);
    }

    internal event Action? RestartRequested;

    internal IReadOnlyList<string> TrayMenuLabels => trayIcon.ContextMenuStrip?.Items
        .OfType<ToolStripMenuItem>()
        .Select(item => item.Text ?? string.Empty)
        .ToArray() ?? [];

    internal void SetState(SupervisorState nextState)
    {
        state = nextState;
        (statusTitle.Text, statusDetail.Text) = nextState switch
        {
            SupervisorState.Starting => ("正在启动本机服务", "请稍候，Dada 正在检查 API、Worker 和本地数据。"),
            SupervisorState.Ready => ("Dada 已就绪", "本机服务运行正常。"),
            SupervisorState.StartupFailed => ("Dada 启动失败", $"服务未能启动 · {DateTimeOffset.Now:HH:mm:ss} · 关联 ID {correlationId}\n诊断信息已移除敏感内容。"),
            SupervisorState.PortInUse => ("Dada 无法使用固定端口启动", "固定端口 43121 已被占用，Dada 不会改用其他端口。"),
            SupervisorState.ApiDegraded => ("本机 API 不可用", "网页暂时无法使用，可打开诊断或重新启动服务。"),
            SupervisorState.WorkerDegraded => ("后台服务暂时不可用", "可继续查看、下载、编辑和删除；暂不能创建新任务。"),
            SupervisorState.StorageFull => ("本地存储空间已满", "可继续查看和导出；清理空间后再创建新内容。"),
            SupervisorState.StorageUnavailable => ("本地数据暂时不可用", "请恢复已配置的数据目录，然后重试。"),
            SupervisorState.DiagnosticsRunning => ("正在运行诊断", "正在检查本机服务状态。"),
            SupervisorState.DiagnosticsReady => ("诊断已完成", "可复制已脱敏的诊断结果。"),
            _ => throw new ArgumentOutOfRangeException(nameof(nextState)),
        };
        trayIcon.Text = TrayText(nextState);
        SetStatusLabel(apiStatus, ApiStatus(nextState));
        SetStatusLabel(workerStatus, WorkerStatus(nextState));
        SetStatusLabel(storageStatus, StorageStatus(nextState));
        var allowed = SupervisorActions.For(nextState);
        openMenuItem.Enabled = allowed.Contains(SupervisorAction.OpenProduct);
        diagnosticsMenuItem.Enabled = allowed.Contains(SupervisorAction.OpenDiagnostics);
        restartMenuItem.Visible = allowed.Contains(SupervisorAction.RestartServices);
        RenderActions();
    }

    internal void RestoreWindow()
    {
        Show();
        WindowState = FormWindowState.Normal;
        Activate();
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing) trayIcon.Dispose();
        base.Dispose(disposing);
    }

    private void RenderActions()
    {
        actions.SuspendLayout();
        actions.Controls.Clear();
        foreach (var action in SupervisorActions.For(state))
        {
            var button = new Button
            {
                AutoSize = false,
                BackColor = action is SupervisorAction.Retry or SupervisorAction.OpenProduct ? Color.FromArgb(28, 32, 38) : Color.White,
                FlatStyle = FlatStyle.Flat,
                ForeColor = action is SupervisorAction.Retry or SupervisorAction.OpenProduct ? Color.White : Color.FromArgb(33, 37, 43),
                Height = 38,
                Margin = new Padding(4),
                Text = ActionLabel(action),
                Width = action switch
                {
                    SupervisorAction.OpenChrome => 132,
                    SupervisorAction.OpenEdge => 122,
                    SupervisorAction.OpenDiagnostics => 122,
                    SupervisorAction.CheckExistingInstance => 132,
                    SupervisorAction.RestartServices => 122,
                    _ => 112,
                },
            };
            button.FlatAppearance.BorderColor = Color.FromArgb(215, 219, 225);
            button.Click += (_, _) => InvokeAction(action);
            actions.Controls.Add(button);
        }
        actions.ResumeLayout();
    }

    private void InvokeAction(SupervisorAction action)
    {
        switch (action)
        {
            case SupervisorAction.OpenProduct:
                OpenProduct();
                break;
            case SupervisorAction.OpenChrome:
                SupportedBrowserLauncher.OpenChrome(LoopbackEndpoint.ProductUri);
                break;
            case SupervisorAction.OpenEdge:
                SupportedBrowserLauncher.OpenEdge(LoopbackEndpoint.ProductUri);
                break;
            case SupervisorAction.OpenDiagnostics:
            case SupervisorAction.CheckExistingInstance:
                ShowDiagnostics();
                break;
            case SupervisorAction.Retry:
            case SupervisorAction.RestartServices:
                SetState(SupervisorState.Starting);
                RestartRequested?.Invoke();
                break;
            case SupervisorAction.Exit:
                Close();
                break;
        }
    }

    internal static bool OpenProductInSupportedBrowser() => SupportedBrowserLauncher.OpenDefaultSupported(LoopbackEndpoint.ProductUri);

    private static void OpenProduct() => OpenProductInSupportedBrowser();

    private void ShowDiagnostics()
    {
        using var diagnostics = new DiagnosticsForm(state);
        diagnostics.ShowDialog(this);
    }

    private static Label AddStatusRow(TableLayoutPanel panel, int row, string label, string status)
    {
        panel.Controls.Add(new Label { Anchor = AnchorStyles.Left, AutoSize = true, Font = new Font("Microsoft YaHei UI", 10F), Text = label }, 0, row);
        var statusLabel = new Label { Anchor = AnchorStyles.Right, AutoSize = true, Font = new Font("Microsoft YaHei UI", 9F), Text = status };
        SetStatusLabel(statusLabel, status);
        panel.Controls.Add(statusLabel, 1, row);
        return statusLabel;
    }

    private static void SetStatusLabel(Label label, string status)
    {
        label.Text = status;
        label.ForeColor = status is "可用" or "正常" ? Color.FromArgb(28, 122, 76) : Color.FromArgb(180, 86, 32);
    }

    private static string ApiStatus(SupervisorState value) => value is SupervisorState.ApiDegraded or SupervisorState.StartupFailed or SupervisorState.PortInUse ? "不可用" : value == SupervisorState.Starting ? "启动中" : "正常";
    private static string WorkerStatus(SupervisorState value) => value is SupervisorState.WorkerDegraded or SupervisorState.StartupFailed or SupervisorState.PortInUse ? "不可用" : value == SupervisorState.Starting ? "启动中" : "正常";
    private static string TrayText(SupervisorState value) => value switch
    {
        SupervisorState.Starting => "Dada - 正在启动",
        SupervisorState.Ready => "Dada - 运行正常",
        SupervisorState.ApiDegraded => "Dada - API 不可用",
        SupervisorState.WorkerDegraded => "Dada - Worker 不可用",
        SupervisorState.StorageFull => "Dada - 存储空间已满",
        SupervisorState.StorageUnavailable => "Dada - 本地数据不可用",
        SupervisorState.PortInUse => "Dada - 固定端口被占用",
        SupervisorState.StartupFailed => "Dada - 启动失败",
        _ => "Dada - 运行诊断",
    };

    private static string StorageStatus(SupervisorState value) => value switch
    {
        SupervisorState.StorageFull => "已满",
        SupervisorState.StorageUnavailable => "不可用",
        _ => "可用",
    };

    private static string ActionLabel(SupervisorAction action) => action switch
    {
        SupervisorAction.OpenProduct => "打开 Dada",
        SupervisorAction.OpenChrome => "使用 Chrome 打开",
        SupervisorAction.OpenEdge => "使用 Edge 打开",
        SupervisorAction.OpenDiagnostics => "打开诊断",
        SupervisorAction.CheckExistingInstance => "检查已有 Dada",
        SupervisorAction.Retry => "重试",
        SupervisorAction.RestartServices => "重新启动服务",
        SupervisorAction.Exit => "退出 Dada",
        _ => throw new ArgumentOutOfRangeException(nameof(action)),
    };
}

internal sealed class DiagnosticsForm : Form
{
    internal string CopyPayload { get; }

    internal DiagnosticsForm(SupervisorState state)
    {
        BackColor = Color.White;
        ClientSize = new Size(760, 440);
        Font = new Font("Microsoft YaHei UI", 9F);
        StartPosition = FormStartPosition.CenterParent;
        Text = "Dada 运行诊断";
        var title = new Label { AutoSize = true, Font = new Font("Microsoft YaHei UI", 15F, FontStyle.Bold), Location = new Point(28, 24), Text = "运行诊断" };
        var detail = new Label { ForeColor = Color.FromArgb(91, 97, 105), Location = new Point(30, 62), Size = new Size(700, 42), Text = "诊断仅包含服务状态、固定端口、目录可用性和凭据是否已配置，不包含凭据值或用户内容。" };
        var checks = new ListView { Location = new Point(30, 116), Size = new Size(700, 250), View = View.Details, FullRowSelect = true };
        checks.Columns.Add("组件", 170);
        checks.Columns.Add("状态", 110);
        checks.Columns.Add("检查结果", 390);
        var report = DiagnosticReport.Create(
            state,
            [
                state == SupervisorState.StorageUnavailable
                    ? DiagnosticCheck.Fail("log_writable", "log_write_failed")
                    : DiagnosticCheck.Pass("log_writable", "log_ready"),
                DiagnosticCheck.Pass("fixed_port", "loopback_only"),
            ],
            new DiagnosticStorageSummary(0, 0, 0),
            []);
        CopyPayload = System.Text.Json.JsonSerializer.Serialize(report, new System.Text.Json.JsonSerializerOptions { WriteIndented = true });
        checks.Items.Add(new ListViewItem(["Supervisor", "正常", StateLabel(state)]));
        checks.Items.Add(new ListViewItem(["固定端口", "43121", "仅绑定 127.0.0.1"]));
        checks.Items.Add(new ListViewItem(["凭据", "已脱敏", "仅显示是否已配置"]));
        var copy = new Button { Location = new Point(592, 382), Size = new Size(138, 36), Text = "复制脱敏结果" };
        copy.Click += (_, _) => Clipboard.SetText(CopyPayload);
        Controls.AddRange([title, detail, checks, copy]);
    }

    private static string StateLabel(SupervisorState state) => state switch
    {
        SupervisorState.Starting => "正在启动",
        SupervisorState.Ready => "运行正常",
        SupervisorState.StartupFailed => "启动失败",
        SupervisorState.PortInUse => "固定端口被占用",
        SupervisorState.ApiDegraded => "API 不可用",
        SupervisorState.WorkerDegraded => "Worker 不可用",
        SupervisorState.StorageFull => "存储空间已满",
        SupervisorState.StorageUnavailable => "本地数据不可用",
        SupervisorState.DiagnosticsRunning => "诊断中",
        SupervisorState.DiagnosticsReady => "诊断完成",
        _ => "未知",
    };
}
