using FontStashSharp;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Theriapolis.Game.CodexUI.Core;

namespace Theriapolis.Game.CodexUI.Widgets;

public enum CodexButtonVariant { Primary, Ghost, Small }

/// <summary>
/// Codex-styled push button. The three variants match the React design's
/// <c>.btn.primary / .btn.ghost / .btn.small</c>:
///   - Primary: gilded fill on parchment, used for Confirm + Next-style actions.
///   - Ghost:   ink border, transparent fill, used for Back + secondary actions.
///   - Small:   smaller padding/font, used inline (Reroll / Auto-assign / Clear).
/// </summary>
public sealed class CodexButton : CodexWidget
{
    public string Text { get; set; }
    public CodexButtonVariant Variant { get; set; }
    public System.Action? OnClick { get; set; }
    public int? FixedWidth { get; set; }

    private readonly CodexAtlas _atlas;
    private readonly SpriteFontBase _font;
    private bool _hovered;
    private bool _pressed;

    public CodexButton(string text, CodexAtlas atlas, CodexButtonVariant variant = CodexButtonVariant.Ghost,
                       System.Action? onClick = null, int? fixedWidth = null)
    {
        Text = text;
        _atlas = atlas;
        Variant = variant;
        OnClick = onClick;
        FixedWidth = fixedWidth;
        _font = variant == CodexButtonVariant.Small ? CodexFonts.MonoTag : CodexFonts.DisplaySmall;
    }

    protected override Point MeasureCore(Point available)
    {
        var s = _font.MeasureString(Text);
        int padX = Variant == CodexButtonVariant.Small ? 12 : 22;
        int padY = Variant == CodexButtonVariant.Small ? 6  : 10;
        int w = FixedWidth ?? ((int)s.X + padX * 2);
        int h = (int)System.MathF.Ceiling(_font.LineHeight) + padY * 2;
        return new Point(System.Math.Min(w, available.X), h);
    }

    protected override void ArrangeCore(Rectangle bounds) { }

    public override void Update(GameTime gt, CodexInput input)
    {
        bool wasHovered = _hovered;
        _hovered = ContainsPoint(input.MousePosition);
        if (_hovered && input.LeftJustPressed)  _pressed = true;
        if (input.LeftJustReleased)
        {
            if (_pressed && _hovered && Enabled) OnClick?.Invoke();
            _pressed = false;
        }
    }

    public override void Draw(SpriteBatch sb, GameTime gt)
    {
        Color fill, border, textColor;
        switch (Variant)
        {
            case CodexButtonVariant.Primary:
                fill = _hovered ? CodexColors.Seal2 : CodexColors.Seal;
                border = CodexColors.Seal2;
                textColor = CodexColors.Bg;
                break;
            case CodexButtonVariant.Ghost:
                fill = _hovered ? CodexColors.Ink : CodexColors.Bg;
                border = CodexColors.Ink;
                textColor = _hovered ? CodexColors.Bg : CodexColors.Ink;
                break;
            default:
                fill = _hovered ? CodexColors.Bg2 : CodexColors.Bg;
                border = CodexColors.Rule;
                textColor = CodexColors.InkSoft;
                break;
        }

        if (!Enabled)
        {
            // 40% opacity per .btn[disabled]
            fill = new Color(fill.R, fill.G, fill.B, (byte)(fill.A * 0.4f));
            textColor = new Color(textColor.R, textColor.G, textColor.B, (byte)(textColor.A * 0.6f));
        }

        // Body fill
        sb.Draw(_atlas.Pixel, Bounds, fill);
        // 1-px border outline
        DrawBorder(sb, Bounds, border, 1);

        var s = _font.MeasureString(Text);
        float tx = Bounds.X + (Bounds.Width  - s.X) / 2f;
        float ty = Bounds.Y + (Bounds.Height - _font.LineHeight) / 2f;
        _font.DrawText(sb, Text, new Vector2(tx, ty), textColor);
    }

    private void DrawBorder(SpriteBatch sb, Rectangle r, Color c, int t)
    {
        sb.Draw(_atlas.Pixel, new Rectangle(r.X, r.Y, r.Width, t), c);
        sb.Draw(_atlas.Pixel, new Rectangle(r.X, r.Bottom - t, r.Width, t), c);
        sb.Draw(_atlas.Pixel, new Rectangle(r.X, r.Y, t, r.Height), c);
        sb.Draw(_atlas.Pixel, new Rectangle(r.Right - t, r.Y, t, r.Height), c);
    }
}
