using Sunfish.Auth;
using Sunfish.Config;

namespace Sunfish.WinForms;

/// <summary>Usuarios y contrasenas (sobre una copia de la configuracion), compacto como la ventana principal.</summary>
internal sealed class UsersDialog : Form
{
    private readonly ListBox _list = new() { Dock = DockStyle.Fill, IntegralHeight = false, Margin = new Padding(0, 0, 0, 6) };
    private readonly ToolTip _tips = new();

    public SunfishConfig Config { get; }
    public bool Changed { get; private set; }

    public UsersDialog(SunfishConfig config)
    {
        Config = config;
        SuspendLayout();
        AutoScaleDimensions = new SizeF(96F, 96F);
        AutoScaleMode = AutoScaleMode.Dpi;
        Text = Texts.UsersTitle;
        FormBorderStyle = FormBorderStyle.SizableToolWindow;
        ShowInTaskbar = false;
        StartPosition = FormStartPosition.CenterParent;
        ClientSize = new Size(210, 200);
        MinimumSize = new Size(200, 180);
        Padding = new Padding(6);

        var add = UiImages.IconButton("plus", Texts.Add, _tips);
        var delete = UiImages.IconButton("minus", Texts.Delete, _tips);
        var password = UiImages.IconButton("pencil", Texts.Password, _tips);
        var close = new Button { Text = Texts.Close, Size = new Size(70, 26), DialogResult = DialogResult.OK, UseVisualStyleBackColor = true };
        AcceptButton = close;
        CancelButton = close;

        // Lista arriba; abajo los botones de imagen a la izquierda y Cerrar a la derecha.
        var grid = new TableLayoutPanel { Dock = DockStyle.Fill, ColumnCount = 5, RowCount = 2, Margin = new Padding(0) };
        for (int i = 0; i < 3; i++)
            grid.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
        grid.ColumnStyles.Add(new ColumnStyle(SizeType.Percent, 100));
        grid.ColumnStyles.Add(new ColumnStyle(SizeType.AutoSize));
        grid.RowStyles.Add(new RowStyle(SizeType.Percent, 100));
        grid.RowStyles.Add(new RowStyle(SizeType.AutoSize));
        grid.Controls.Add(_list, 0, 0);
        grid.SetColumnSpan(_list, 5);
        add.Margin = delete.Margin = password.Margin = new Padding(0, 0, 3, 0);
        close.Margin = new Padding(0);
        grid.Controls.Add(add, 0, 1);
        grid.Controls.Add(delete, 1, 1);
        grid.Controls.Add(password, 2, 1);
        grid.Controls.Add(close, 4, 1);
        Controls.Add(grid);
        ResumeLayout(false);
        PerformLayout();

        add.Click += (_, _) => AddUser();
        password.Click += (_, _) => ChangePassword();
        delete.Click += (_, _) => DeleteUser();
        _list.DoubleClick += (_, _) => ChangePassword();
        Fill();
    }

    private void Fill()
    {
        _list.Items.Clear();
        foreach (var user in Config.Users)
            _list.Items.Add(user.Name);
    }

    private void AddUser()
    {
        using var dialog = new PasswordDialog(Texts.NewUser, askName: true);
        if (dialog.ShowDialog(this) != DialogResult.OK)
            return;
        string name = dialog.UserName;
        if (!ShareSettings.IsValidName(name))
        {
            MessageBox.Show(this, Strings.ErrorBadName(name), Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
            return;
        }
        if (Config.Users.Any(u => u.Name.Equals(name, StringComparison.OrdinalIgnoreCase)))
        {
            MessageBox.Show(this, Strings.ErrorNameInUse(name), Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
            return;
        }
        Config.Users.Add(new UserConfig { Name = name, PasswordHash = Passwords.Hash(dialog.Password) });
        Changed = true;
        Fill();
    }

    private void ChangePassword()
    {
        if (_list.SelectedItem is not string name)
            return;
        using var dialog = new PasswordDialog(Texts.PasswordFor(name), askName: false);
        if (dialog.ShowDialog(this) != DialogResult.OK)
            return;
        Config.Users.First(u => u.Name == name).PasswordHash = Passwords.Hash(dialog.Password);
        Changed = true;
    }

    private void DeleteUser()
    {
        if (_list.SelectedItem is not string name)
            return;
        if (MessageBox.Show(this, Strings.AskDeleteUser(name), Text, MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
            return;
        Config.Users.RemoveAll(u => u.Name == name);
        Changed = true;
        Fill();
    }
}

/// <summary>Pide (opcionalmente un nombre y) una contrasena dos veces; etiquetas encima, como en la 2.0.</summary>
internal sealed class PasswordDialog : Form
{
    private readonly TextBox _name = new() { Width = 200, BackColor = Color.FromArgb(255, 252, 220) };
    private readonly TextBox _password = new() { Width = 200, UseSystemPasswordChar = true };
    private readonly TextBox _repeat = new() { Width = 200, UseSystemPasswordChar = true };
    private readonly bool _askName;

    public string UserName => _name.Text.Trim();
    public string Password => _password.Text;

    public PasswordDialog(string title, bool askName, bool askRepeat = true)
    {
        _askName = askName;
        SuspendLayout();
        AutoScaleDimensions = new SizeF(96F, 96F);
        AutoScaleMode = AutoScaleMode.Dpi;
        Text = title;
        FormBorderStyle = FormBorderStyle.FixedDialog;
        MaximizeBox = MinimizeBox = false;
        ShowInTaskbar = false;
        StartPosition = FormStartPosition.CenterParent;
        AutoSize = true;
        AutoSizeMode = AutoSizeMode.GrowAndShrink;
        Padding = new Padding(10, 6, 10, 10);

        var main = new FlowLayoutPanel { FlowDirection = FlowDirection.TopDown, AutoSize = true, WrapContents = false };
        if (askName)
        {
            main.Controls.Add(Caption(Texts.UserName));
            main.Controls.Add(_name);
        }
        main.Controls.Add(Caption(Texts.PasswordLabel));
        main.Controls.Add(_password);
        if (askRepeat)
        {
            main.Controls.Add(Caption(Texts.RepeatLabel));
            main.Controls.Add(_repeat);
        }

        var ok = new Button { Text = Texts.Ok, Size = new Size(70, 25), UseVisualStyleBackColor = true };
        ok.Click += (_, _) => Accept();
        var cancel = new Button { Text = Texts.Cancel, Size = new Size(70, 25), DialogResult = DialogResult.Cancel, UseVisualStyleBackColor = true };
        var buttons = new FlowLayoutPanel { FlowDirection = FlowDirection.RightToLeft, Width = 206, Height = 32, Margin = new Padding(0, 10, 0, 0) };
        buttons.Controls.AddRange([cancel, ok]);
        main.Controls.Add(buttons);
        AcceptButton = ok;
        CancelButton = cancel;
        Controls.Add(main);
        ResumeLayout(false);
        PerformLayout();
    }

    private static Label Caption(string text) =>
        new() { Text = text, AutoSize = true, Margin = new Padding(3, 6, 3, 1) };

    private void Accept()
    {
        if (_askName && UserName.Length == 0)
            return;
        if (_password.Text.Length == 0)
        {
            MessageBox.Show(this, Strings.ErrorPasswordEmpty, Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
            return;
        }
        if (_repeat.Parent is not null && _password.Text != _repeat.Text)
        {
            MessageBox.Show(this, Texts.PasswordMismatch, Text, MessageBoxButtons.OK, MessageBoxIcon.Warning);
            return;
        }
        DialogResult = DialogResult.OK;
    }
}
