using Sunfish.Host;
using Sunfish.Log;
using Sunfish.Net;

namespace Sunfish.WinForms;

/// <summary>
/// Ventana de informacion (boton "i", como en la 2.0): direcciones de cada share por interfaz
/// (doble clic las abre, menu para copiarlas), el registro y el "acerca de".
/// </summary>
internal sealed class InfoForm : Form
{
    private readonly SunfishHost _host;
    private readonly ListBox _addresses = new() { IntegralHeight = false, HorizontalScrollbar = true, Dock = DockStyle.Fill };
    private readonly ListBox _log = new() { IntegralHeight = false, HorizontalScrollbar = true, Dock = DockStyle.Fill };
    private readonly LinkLabel _adminLink = new() { AutoSize = true };
    private readonly Action<LogEntry> _onLog;
    private readonly Action _onChange;

    public InfoForm(SunfishHost host)
    {
        _host = host;
        SuspendLayout();
        AutoScaleDimensions = new SizeF(96F, 96F);
        AutoScaleMode = AutoScaleMode.Dpi;
        Text = "Sunfish";
        ClientSize = new Size(360, 420);
        MinimumSize = new Size(300, 300);
        StartPosition = FormStartPosition.CenterParent;
        ShowInTaskbar = false;
        MinimizeBox = false;
        Padding = new Padding(6);

        using (var stream = typeof(InfoForm).Assembly.GetManifestResourceStream("ui/logo.png"))
        {
            var header = new Panel { Dock = DockStyle.Top, Height = 56 };
            if (stream is not null)
                header.Controls.Add(new PictureBox { Image = Image.FromStream(stream), SizeMode = PictureBoxSizeMode.Zoom, Bounds = new Rectangle(0, 2, 48, 48) });
            header.Controls.Add(new Label { Text = Texts.About, AutoSize = true, Location = new Point(56, 8) });
            _adminLink.Location = new Point(56, 30);
            _adminLink.LinkClicked += async (_, _) => await EditAdminAsync();
            header.Controls.Add(_adminLink);
            Controls.Add(header);
        }

        var addressesBox = new GroupBox { Text = Strings.AppAddresses, Dock = DockStyle.Fill, Padding = new Padding(4) };
        addressesBox.Controls.Add(_addresses);
        var logBox = new GroupBox { Text = Strings.AppLog, Dock = DockStyle.Fill, Padding = new Padding(4) };
        logBox.Controls.Add(_log);
        var split = new SplitContainer { Dock = DockStyle.Fill, Orientation = Orientation.Horizontal, SplitterDistance = 150 };
        split.Panel1.Controls.Add(addressesBox);
        split.Panel2.Controls.Add(logBox);
        Controls.Add(split);
        split.BringToFront();
        ResumeLayout(false);
        PerformLayout();

        var menu = new ContextMenuStrip();
        menu.Items.Add(Texts.OpenInBrowser, UiImages.Get("share"), (_, _) => MainForm.Open(SelectedUrl()));
        menu.Items.Add(Texts.CopyUrl, null, (_, _) =>
        {
            if (SelectedUrl() is { } url)
                Clipboard.SetText(url);
        });
        menu.Items.Add(Texts.ShowQr, null, (_, _) =>
        {
            if (SelectedUrl() is { } url)
                UiPlacement.ShowCentered(new QrForm(url) { Icon = Icon }, this);
        });
        _addresses.ContextMenuStrip = menu;
        _addresses.DoubleClick += (_, _) => MainForm.Open(SelectedUrl());

        _onLog = entry => SafeInvoke(() => AddLog(entry));
        _onChange = () => SafeInvoke(FillAddresses);
        _host.Log.Written += _onLog;
        _host.Changed += _onChange;
        foreach (var entry in _host.Log.Recent())
            AddLog(entry);
        FillAddresses();
    }

    protected override void OnFormClosed(FormClosedEventArgs e)
    {
        _host.Log.Written -= _onLog;
        _host.Changed -= _onChange;
        base.OnFormClosed(e);
    }

    private string? SelectedUrl()
    {
        if (_addresses.SelectedItem is not string text)
            return null;
        int space = text.IndexOf(' ');
        bool url = text.StartsWith("http", StringComparison.Ordinal) || text.StartsWith("ftp", StringComparison.Ordinal);
        return url ? (space < 0 ? text : text[..space]) : null;
    }

    private async Task EditAdminAsync()
    {
        var copy = _host.Config.Clone();
        using var dialog = new AdminDialog(copy.Admin, _host.HttpPort);
        if (dialog.ShowDialog(this) == DialogResult.OK)
            await (copy.FilePath is null ? _host.ApplyAsync(copy) : _host.SaveAndApplyAsync(copy));
    }

    private void FillAddresses()
    {
        _adminLink.Text = Texts.AdminLink(_host.Config.Admin.Enabled && _host.Config.Admin.PasswordHash.Length > 0);
        _addresses.Items.Clear();
        if (!_host.HttpRunning && !_host.FtpRunning)
        {
            _addresses.Items.Add(Strings.AppStopped);
            return;
        }
        var addresses = LocalAddresses.Get();
        if (addresses.Count == 0)
            _addresses.Items.Add(Texts.NoAddress);
        foreach (var share in _host.Config.Shares.Where(s => s.Enabled))
        {
            _addresses.Items.Add($"[{share.Name}]");
            foreach (var address in addresses)
            {
                if (_host.HttpRunning)
                    _addresses.Items.Add($"{LocalAddresses.HttpUrl(address.Address, _host.HttpPort, share)}   ({address.Interface})");
                if (_host.FtpRunning && share.Ftp)
                    _addresses.Items.Add($"{LocalAddresses.FtpUrl(address.Address, _host.FtpPort, share)}   ({address.Interface})");
            }
        }
    }

    private void AddLog(LogEntry entry)
    {
        _log.Items.Add($"{entry.Time:HH:mm:ss} {entry.Message}");
        while (_log.Items.Count > 1000)
            _log.Items.RemoveAt(0);
        _log.TopIndex = Math.Max(0, _log.Items.Count - 1);
    }

    private void SafeInvoke(Action action)
    {
        if (IsDisposed || !IsHandleCreated)
            return;
        try
        {
            BeginInvoke(action);
        }
        catch (InvalidOperationException) { }
    }
}
