using FontStashSharp;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Theriapolis.Core.Data;
using Theriapolis.Game.CodexUI.Core;
using Theriapolis.Game.CodexUI.Screens;
using Theriapolis.Game.CodexUI.Widgets;
using Theriapolis.Game.UI;

namespace Theriapolis.Game.CodexUI.Steps;

/// <summary>
/// Step I — Clade. Two grouped grids (Predators / Prey) of clade cards,
/// each card showing the clade name + kind + ability mods + language chips
/// + trait chips. Selection swaps the species default to the first species
/// belonging to that clade.
/// </summary>
public static class StepClade
{
    public static CodexWidget Build(CodexCharacterCreationScreen s, CodexAtlas atlas, CodexHoverPopover popover)
    {
        var col = new Column { Spacing = 14 };
        col.Add(StepCommon.PageIntro("Folio I — Of Bloodlines", "Choose your Clade",
            "The seven great families of Theriapolis. Your clade is the body you were born to — the broad shape of your gait, the fall of your shadow, the words your scent carries before you speak."));

        col.Add(new CodexLabel("PREDATORS", CodexFonts.MonoTag, CodexColors.InkMute));
        col.Add(BuildGrid(s, atlas, popover, "predator"));
        col.Add(new CodexLabel("PREY", CodexFonts.MonoTag, CodexColors.InkMute));
        col.Add(BuildGrid(s, atlas, popover, "prey"));
        return col;
    }

    private static CodexWidget BuildGrid(CodexCharacterCreationScreen s, CodexAtlas atlas, CodexHoverPopover popover, string kind)
    {
        var grid = new Grid { Columns = 3 };
        foreach (var c in s.Clades)
        {
            if (c.Kind != kind) continue;
            grid.Add(BuildCard(s, atlas, popover, c));
        }
        return grid;
    }

    private static CodexWidget BuildCard(CodexCharacterCreationScreen s, CodexAtlas atlas, CodexHoverPopover popover, CladeDef c)
    {
        var content = new Column { Spacing = 8 };

        // Header: sigil + name/kind
        var headerRow = new Row { Spacing = 12, VAlignChildren = VAlign.Top };
        headerRow.Add(new SigilWidget(atlas, c.Id));
        var titleCol = new Column { Spacing = 2 };
        titleCol.Add(new CodexLabel(c.Name, CodexFonts.DisplayMedium, CodexColors.Ink));
        titleCol.Add(new CodexLabel(c.Kind.ToUpperInvariant(), CodexFonts.MonoTagSmall, CodexColors.InkMute));
        headerRow.Add(titleCol);
        content.Add(headerRow);

        // Mods row
        if (c.AbilityMods.Count > 0)
        {
            var mods = new WrapRow();
            foreach (var kv in c.AbilityMods)
                mods.Add(new ModChipMini(atlas, kv.Key, kv.Value));
            content.Add(mods);
        }

        // Languages
        content.Add(new CodexLabel("LANGUAGES", CodexFonts.MonoTagSmall, CodexColors.InkMute));
        var langs = new WrapRow();
        foreach (var l in c.Languages)
            langs.Add(new HoverableChip(atlas, popover, CodexCopy.LanguageName(l), CodexCopy.LanguageName(l), CodexCopy.LanguageDescription(l), null, ChipKind.Language));
        content.Add(langs);

        // Traits
        content.Add(new CodexLabel("TRAITS", CodexFonts.MonoTagSmall, CodexColors.InkMute));
        var traits = new WrapRow();
        foreach (var t in c.Traits)
            traits.Add(new HoverableChip(atlas, popover, t.Name, t.Name, t.Description, null, ChipKind.Trait));
        foreach (var t in c.Detriments)
            traits.Add(new HoverableChip(atlas, popover, t.Name, t.Name, t.Description, "DETRIMENT", ChipKind.TraitDetriment));
        content.Add(traits);

        bool isSelected = s.Clade == c;
        var card = new CodexCard(atlas, content, isSelected,
            onClick: () =>
            {
                s.Clade = c;
                // If the previously-picked species belongs to a different
                // clade, drop it — but never auto-pick a new species. The
                // user must visit the Species folio explicitly so the
                // Calling step stays locked behind that decision.
                if (s.Species is not null && s.Species.CladeId != c.Id)
                    s.Species = null;
                s.InvalidateLayout();
            });
        card.CornerSigil = atlas.SigilFor(c.Id);
        return card;
    }
}

/// <summary>Renders the clade sigil placeholder + a centred initial letter.</summary>
internal sealed class SigilWidget : CodexWidget
{
    private readonly CodexAtlas _atlas;
    private readonly string _cladeId;
    public SigilWidget(CodexAtlas atlas, string cladeId) { _atlas = atlas; _cladeId = cladeId; }

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

    public override void Draw(SpriteBatch sb, GameTime gt)
    {
        sb.Draw(_atlas.SigilFor(_cladeId), Bounds, Color.White);
        // Letter overlay so the placeholder is identifiable.
        char ch = char.ToUpper(_cladeId.Length > 0 ? _cladeId[0] : '?');
        var font = CodexFonts.DisplayMedium;
        var s = font.MeasureString(ch.ToString());
        font.DrawText(sb, ch.ToString(),
                      new Vector2(Bounds.X + (Bounds.Width - s.X) / 2f, Bounds.Y + (Bounds.Height - font.LineHeight) / 2f),
                      CodexColors.Ink);
    }
}

/// <summary>Inline mod pill drawn as `STR +1` / `DEX -1` etc.</summary>
internal sealed class ModChipMini : CodexWidget
{
    private readonly CodexAtlas _atlas;
    private readonly string _label;
    private readonly bool _positive;
    private readonly SpriteFontBase _font = CodexFonts.MonoTagSmall;

    public ModChipMini(CodexAtlas atlas, string ab, int v)
    {
        _atlas = atlas;
        _label = $"{ab} {(v >= 0 ? "+" : "")}{v}";
        _positive = v >= 0;
    }

    protected override Point MeasureCore(Point available)
    {
        var s = _font.MeasureString(_label);
        return new Point((int)s.X + 14, (int)System.MathF.Ceiling(_font.LineHeight) + 6);
    }
    protected override void ArrangeCore(Rectangle bounds) { }

    public override void Draw(SpriteBatch sb, GameTime gt)
    {
        var border = _positive ? CodexColors.Seal : CodexColors.InkMute;
        var fill = CodexColors.Bg;
        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);
        var s = _font.MeasureString(_label);
        _font.DrawText(sb, _label,
                       new Vector2(Bounds.X + (Bounds.Width - s.X) / 2f, Bounds.Y + (Bounds.Height - _font.LineHeight) / 2f),
                       _positive ? CodexColors.Seal : CodexColors.InkSoft);
    }
}

/// <summary>
/// Chip variant that drives the screen's popover when hovered. Acts as a
/// thin wrapper around <see cref="CodexChip"/> plus a popover-show side
/// effect during update.
/// </summary>
internal sealed class HoverableChip : CodexWidget
{
    private readonly CodexChip _chip;
    private readonly CodexHoverPopover _popover;
    private readonly string _title, _body;
    private readonly string? _tag;
    private readonly bool _detriment;

    public HoverableChip(CodexAtlas atlas, CodexHoverPopover popover, string text,
                         string popTitle, string popBody, string? popTag, ChipKind kind)
    {
        _chip = new CodexChip(text, kind, atlas, popTitle, popBody, popTag);
        _popover = popover;
        _title = popTitle;
        _body = popBody;
        _tag = popTag;
        _detriment = kind == ChipKind.TraitDetriment;
    }

    public System.Action? OnClick { get => _chip.OnClick; set => _chip.OnClick = value; }

    protected override Point MeasureCore(Point available) => _chip.Measure(available);
    protected override void ArrangeCore(Rectangle bounds) => _chip.Arrange(bounds);

    public override void Update(GameTime gt, CodexInput input)
    {
        _chip.Update(gt, input);
        if (_chip.IsHovered && !string.IsNullOrEmpty(_body))
            _popover.Show(_chip.Bounds, _title, _body, _tag, _detriment);
    }

    public override void Draw(SpriteBatch sb, GameTime gt) => _chip.Draw(sb, gt);
}

/// <summary>Reusable page-intro block: small mono eyebrow, large display title, body paragraph.</summary>
public static class StepCommon
{
    public static CodexWidget PageIntro(string eyebrow, string title, string body)
    {
        var col = new Column { Spacing = 6 };
        col.Add(new CodexLabel(eyebrow.ToUpperInvariant(), CodexFonts.MonoTag, CodexColors.InkMute));
        col.Add(new CodexLabel(title, CodexFonts.DisplayLarge, CodexColors.Ink));
        col.Add(new CodexLabel(body, CodexFonts.SerifBody, CodexColors.InkSoft));
        return new Padding(col, new Thickness(0, 0, 0, 14));
    }
}
