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

namespace Theriapolis.Game.CodexUI.Widgets;

/// <summary>
/// Single-line text input. The display matches the React design's
/// <c>input[type=text]</c>: serif-display font, transparent background,
/// gilded underline rule that lights up while focused. Used for the name
/// field in the Sign step.
///
/// Receives characters via <see cref="CodexInput.TextEnteredThisFrame"/>;
/// the parent screen subscribes to <c>Window.TextInput</c> in Initialize
/// and routes them through <see cref="CodexInput.OnTextInput"/>. Backspace,
/// Enter and arrow keys are handled in <see cref="Update"/>.
/// </summary>
public sealed class CodexTextBox : CodexWidget
{
    public string Text { get; set; } = "";
    public string Placeholder { get; set; } = "";
    public bool IsFocused { get; set; }
    public int? FixedWidth { get; set; }
    public System.Action<string>? OnChanged { get; set; }

    private readonly CodexAtlas _atlas;
    private readonly SpriteFontBase _font;
    private float _caretBlink;

    public CodexTextBox(string initial, CodexAtlas atlas, int? fixedWidth = null, System.Action<string>? onChanged = null)
    {
        Text = initial;
        _atlas = atlas;
        FixedWidth = fixedWidth;
        OnChanged = onChanged;
        _font = CodexFonts.DisplayMedium;
    }

    protected override Point MeasureCore(Point available)
    {
        int w = FixedWidth ?? System.Math.Min(480, available.X);
        int h = (int)System.MathF.Ceiling(_font.LineHeight) + 14;
        return new Point(w, h);
    }

    protected override void ArrangeCore(Rectangle bounds) { }

    public override void Update(GameTime gt, CodexInput input)
    {
        if (input.LeftJustPressed) IsFocused = ContainsPoint(input.MousePosition);
        _caretBlink = (_caretBlink + (float)gt.ElapsedGameTime.TotalSeconds) % 1f;
        if (!IsFocused) return;

        bool changed = false;
        if (!string.IsNullOrEmpty(input.TextEnteredThisFrame))
        {
            Text += input.TextEnteredThisFrame;
            changed = true;
        }
        if (input.KeyJustPressed(Keys.Back) && Text.Length > 0)
        {
            Text = Text.Substring(0, Text.Length - 1);
            changed = true;
        }
        if (input.KeyJustPressed(Keys.Enter)) IsFocused = false;
        if (changed) OnChanged?.Invoke(Text);
    }

    public override void Draw(SpriteBatch sb, GameTime gt)
    {
        // Background — none (transparent) per design; we just paint the underline.
        Color underline = IsFocused ? CodexColors.Gild : CodexColors.Rule;
        sb.Draw(_atlas.Pixel, new Rectangle(Bounds.X, Bounds.Bottom - 2, Bounds.Width, 2), underline);

        bool empty = string.IsNullOrEmpty(Text);
        string display = empty ? Placeholder : Text;
        Color textColor = empty ? CodexColors.InkMute : CodexColors.Ink;
        _font.DrawText(sb, display, new Vector2(Bounds.X + 4, Bounds.Y + 6), textColor);

        // Caret blink — top-aligned, follows the end of the text.
        if (IsFocused && _caretBlink < 0.5f)
        {
            float caretX = Bounds.X + 4 + _font.MeasureString(Text).X;
            sb.Draw(_atlas.Pixel, new Rectangle((int)caretX, Bounds.Y + 6, 1, (int)_font.LineHeight), CodexColors.Gild);
        }
    }
}
