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

namespace Theriapolis.Game.CodexUI.Widgets;

public enum CheckboxState { Default, Checked, LockedFromBg, Unavailable }

/// <summary>
/// One row of the skill picker. Visual states mirror the React design:
///   - Default:     small ink-mute checkbox, hover gilds
///   - Checked:     seal-red filled checkbox + ✓
///   - LockedFromBg: gild-filled checkbox + ✓ (sealed by background, can't toggle)
///   - Unavailable: dashed underline, faded text — class doesn't offer it
/// Click toggles only when state is Default or Checked.
/// </summary>
public sealed class CodexCheckboxRow : CodexWidget
{
    public string Label { get; set; }
    public string SourceTag { get; set; }
    public CheckboxState State { get; set; }
    public System.Action? OnClick { get; set; }
    public System.Action? OnHover { get; set; }

    private readonly CodexAtlas _atlas;
    private readonly SpriteFontBase _font = CodexFonts.SerifBody;
    private readonly SpriteFontBase _tagFont = CodexFonts.MonoTagSmall;
    private bool _hovered;

    public CodexCheckboxRow(string label, string sourceTag, CheckboxState state, CodexAtlas atlas)
    {
        Label = label;
        SourceTag = sourceTag;
        State = state;
        _atlas = atlas;
    }

    protected override Point MeasureCore(Point available) => new(available.X, 28);
    protected override void ArrangeCore(Rectangle bounds) { }

    public override void Update(GameTime gt, CodexInput input)
    {
        _hovered = ContainsPoint(input.MousePosition);
        // OnHover fires every frame the cursor is over the row, not just
        // on hover-enter. The popover is shown only while a trigger calls
        // Show() each frame (CodexHoverPopover.IsShown decays in one tick
        // when no trigger requests it), so a single transition-only call
        // would flash the popover for one frame and then hide it.
        if (_hovered) OnHover?.Invoke();
        if (_hovered && input.LeftJustReleased)
        {
            if (State == CheckboxState.Default || State == CheckboxState.Checked) OnClick?.Invoke();
        }
    }

    public override void Draw(SpriteBatch sb, GameTime gt)
    {
        // Bottom rule
        sb.Draw(_atlas.Pixel, new Rectangle(Bounds.X, Bounds.Bottom - 1, Bounds.Width, 1),
                new Color(CodexColors.Rule.R, CodexColors.Rule.G, CodexColors.Rule.B, (byte)80));

        // Checkbox
        int boxSize = 18;
        var box = new Rectangle(Bounds.X + 2, Bounds.Y + (Bounds.Height - boxSize) / 2, boxSize, boxSize);
        Color boxFill, boxBorder, checkColor = CodexColors.Bg;
        switch (State)
        {
            case CheckboxState.Checked:
                boxFill = CodexColors.Seal;
                boxBorder = CodexColors.Seal;
                break;
            case CheckboxState.LockedFromBg:
                boxFill = CodexColors.Gild;
                boxBorder = CodexColors.Gild;
                break;
            case CheckboxState.Unavailable:
                boxFill = Color.Transparent;
                boxBorder = new Color(CodexColors.InkMute.R, CodexColors.InkMute.G, CodexColors.InkMute.B, (byte)90);
                break;
            default:
                boxFill = Color.Transparent;
                boxBorder = _hovered ? CodexColors.Gild : CodexColors.InkMute;
                break;
        }
        sb.Draw(_atlas.Pixel, box, boxFill);
        DrawBorder(sb, box, boxBorder, 1);

        if (State == CheckboxState.Checked || State == CheckboxState.LockedFromBg)
        {
            string mark = "✓";
            var s = _font.MeasureString(mark);
            _font.DrawText(sb, mark, new Vector2(box.X + (box.Width - s.X) / 2f, box.Y + (box.Height - _font.LineHeight) / 2f),
                           checkColor);
        }

        // Label text
        Color labelColor = State switch
        {
            CheckboxState.Checked      => CodexColors.Seal,
            CheckboxState.LockedFromBg => CodexColors.Gild,
            CheckboxState.Unavailable  => new Color(CodexColors.InkMute.R, CodexColors.InkMute.G, CodexColors.InkMute.B, (byte)90),
            _                          => _hovered ? CodexColors.Gild : CodexColors.Ink,
        };
        _font.DrawText(sb, Label, new Vector2(box.Right + 8, Bounds.Y + (Bounds.Height - _font.LineHeight) / 2f), labelColor);

        // Right-aligned source tag
        if (!string.IsNullOrEmpty(SourceTag))
        {
            var ts = _tagFont.MeasureString(SourceTag);
            _tagFont.DrawText(sb, SourceTag.ToUpperInvariant(),
                              new Vector2(Bounds.Right - ts.X - 4, Bounds.Y + (Bounds.Height - _tagFont.LineHeight) / 2f),
                              CodexColors.InkMute);
        }
    }

    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);
    }
}
