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

namespace Theriapolis.Game.CodexUI.Widgets;

public enum ChipKind
{
    Trait,        // gild edge, italic serif, default for clade/species/feature traits
    TraitDetriment,
    SkillFromBg,  // gild edge — sealed by background
    SkillFromClass, // seal-red edge — picked from class options
    Language,     // mono-tag pill, ink border
    BgFeature,    // seal-red trait variant for the background card's feature row
}

/// <summary>
/// Pill-shaped chip used for traits, skills, languages, and background feature
/// names. Hovering surfaces a popover with the full description; clicking
/// fires <see cref="OnClick"/> for the few cases the screen needs (skill toggle).
/// </summary>
public sealed class CodexChip : CodexWidget
{
    public string Text { get; set; }
    public string PopoverTitle { get; set; }
    public string PopoverBody { get; set; }
    public string? PopoverTag { get; set; }
    public ChipKind Kind { get; set; }
    public System.Action? OnClick { get; set; }

    private readonly CodexAtlas _atlas;
    private readonly SpriteFontBase _font;

    /// <summary>The screen sets this when the user hovers over us; the screen handles popover layout.</summary>
    public bool IsHovered { get; private set; }

    public CodexChip(string text, ChipKind kind, CodexAtlas atlas,
                     string popoverTitle = "", string popoverBody = "", string? popoverTag = null)
    {
        Text = text;
        Kind = kind;
        _atlas = atlas;
        PopoverTitle = popoverTitle;
        PopoverBody = popoverBody;
        PopoverTag = popoverTag;
        _font = kind == ChipKind.Language ? CodexFonts.MonoTagSmall : CodexFonts.SerifItalic;
    }

    protected override Point MeasureCore(Point available)
    {
        var s = _font.MeasureString(Text);
        return new Point((int)s.X + CodexDensity.ChipPad * 3, (int)System.MathF.Ceiling(_font.LineHeight) + CodexDensity.ChipPad * 2);
    }

    protected override void ArrangeCore(Rectangle bounds) { }

    public override void Update(GameTime gt, CodexInput input)
    {
        IsHovered = ContainsPoint(input.MousePosition);
        if (IsHovered && input.LeftJustReleased) OnClick?.Invoke();
    }

    public override void Draw(SpriteBatch sb, GameTime gt)
    {
        var (fill, border, text) = GetColors();
        if (IsHovered) fill = HoverShift(fill);
        sb.Draw(_atlas.Pixel, Bounds, fill);

        // 1-px outline.
        sb.Draw(_atlas.Pixel, new Rectangle(Bounds.X, Bounds.Y, Bounds.Width, 1), border);
        sb.Draw(_atlas.Pixel, new Rectangle(Bounds.X, Bounds.Bottom - 1, Bounds.Width, 1), border);
        sb.Draw(_atlas.Pixel, new Rectangle(Bounds.X, Bounds.Y, 1, Bounds.Height), border);
        sb.Draw(_atlas.Pixel, new Rectangle(Bounds.Right - 1, Bounds.Y, 1, Bounds.Height), border);

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

    private (Color fill, Color border, Color text) GetColors() => Kind switch
    {
        ChipKind.Trait           => (Mix(CodexColors.Gild, CodexColors.Bg, 0.07f),  Mix(CodexColors.Gild, CodexColors.Rule, 0.55f),  CodexColors.Ink),
        ChipKind.TraitDetriment  => (Mix(CodexColors.Seal, CodexColors.Bg, 0.08f),  Mix(CodexColors.Seal, CodexColors.Rule, 0.55f),  CodexColors.Ink),
        ChipKind.SkillFromBg     => (Mix(CodexColors.Gild, CodexColors.Bg, 0.06f),  Mix(CodexColors.Gild, CodexColors.Rule, 0.60f),  CodexColors.Gild),
        ChipKind.SkillFromClass  => (Mix(CodexColors.Seal, CodexColors.Bg, 0.06f),  Mix(CodexColors.Seal, CodexColors.Rule, 0.55f),  CodexColors.Seal),
        ChipKind.Language        => (CodexColors.Bg,                                  CodexColors.Rule,                                CodexColors.InkSoft),
        ChipKind.BgFeature       => (Mix(CodexColors.Seal, CodexColors.Bg, 0.08f),  Mix(CodexColors.Seal, CodexColors.Rule, 0.55f),  CodexColors.Seal),
        _ => (CodexColors.Bg, CodexColors.Rule, CodexColors.Ink),
    };

    private static Color Mix(Color a, Color b, float t)
        => new(
            (byte)(a.R * t + b.R * (1 - t)),
            (byte)(a.G * t + b.G * (1 - t)),
            (byte)(a.B * t + b.B * (1 - t)),
            (byte)0xFF);

    private static Color HoverShift(Color c)
        => new((byte)System.Math.Min(255, c.R + 14), (byte)System.Math.Min(255, c.G + 14), (byte)System.Math.Min(255, c.B + 14), c.A);
}

/// <summary>
/// Small +N / −N pill that sits next to ability names. Visually a chip with
/// monospace text and seal-red (positive) or ink-mute (negative) chrome.
/// Hover surfaces a popover listing the contributing sources (clade, species).
/// </summary>
public sealed class CodexBonusPill : CodexWidget
{
    public int Total { get; }
    public string PopoverBody { get; set; } = "";

    private readonly CodexAtlas _atlas;
    private readonly SpriteFontBase _font;
    public bool IsHovered { get; private set; }

    public CodexBonusPill(int total, CodexAtlas atlas, string popoverBody = "")
    {
        Total = total;
        _atlas = atlas;
        PopoverBody = popoverBody;
        _font = CodexFonts.MonoTag;
    }

    protected override Point MeasureCore(Point available)
    {
        string label = (Total >= 0 ? "+" : "") + Total.ToString();
        var s = _font.MeasureString(label);
        return new Point((int)s.X + 12, (int)System.MathF.Ceiling(_font.LineHeight) + 6);
    }

    protected override void ArrangeCore(Rectangle bounds) { }

    public override void Update(GameTime gt, CodexInput input) => IsHovered = ContainsPoint(input.MousePosition);

    public override void Draw(SpriteBatch sb, GameTime gt)
    {
        var border = Total >= 0 ? CodexColors.Seal : CodexColors.InkMute;
        var fill = Total >= 0
            ? new Color(CodexColors.Seal.R, CodexColors.Seal.G, CodexColors.Seal.B, (byte)(IsHovered ? 36 : 18))
            : new Color(CodexColors.InkMute.R, CodexColors.InkMute.G, CodexColors.InkMute.B, (byte)(IsHovered ? 28 : 14));
        sb.Draw(_atlas.Pixel, Bounds, fill);
        sb.Draw(_atlas.Pixel, new Rectangle(Bounds.X, Bounds.Y, Bounds.Width, 1), border);
        sb.Draw(_atlas.Pixel, new Rectangle(Bounds.X, Bounds.Bottom - 1, Bounds.Width, 1), border);
        sb.Draw(_atlas.Pixel, new Rectangle(Bounds.X, Bounds.Y, 1, Bounds.Height), border);
        sb.Draw(_atlas.Pixel, new Rectangle(Bounds.Right - 1, Bounds.Y, 1, Bounds.Height), border);

        string label = (Total >= 0 ? "+" : "") + Total.ToString();
        var s = _font.MeasureString(label);
        float tx = Bounds.X + (Bounds.Width  - s.X) / 2f;
        float ty = Bounds.Y + (Bounds.Height - _font.LineHeight) / 2f;
        _font.DrawText(sb, label, new Vector2(tx, ty), border);
    }
}
