using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
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 VII — Sign. Name input + summary panels for lineage / calling /
/// abilities / skills / starting kit. Each panel has an Edit › link that
/// jumps the wizard back to the source step.
/// </summary>
public static class StepReview
{
    public static CodexWidget Build(CodexCharacterCreationScreen s, CodexAtlas atlas, CodexHoverPopover popover)
    {
        var col = new Column { Spacing = 14 };
        col.Add(StepCommon.PageIntro(
            "Folio VII — Of Names & Witness",
            "Sign the Codex",
            "Review your character. The name you sign here is the one the world will speak."));

        // Name input
        var nameBlock = new Column { Spacing = 4 };
        nameBlock.Add(new CodexLabel("NAME", CodexFonts.MonoTag, CodexColors.InkMute));
        var nameInput = new CodexTextBox(s.Name, atlas, fixedWidth: 480, onChanged: t => s.Name = t)
        {
            Placeholder = "Wanderer",
        };
        nameBlock.Add(nameInput);
        col.Add(nameBlock);

        // Lineage / Calling pair
        var pair = new Grid { Columns = 2 };
        pair.Add(BuildBlock(atlas, "Lineage", () => s.Step = 0, s.InvalidateLayout, BuildLineage(s)));
        pair.Add(BuildBlock(atlas, "Calling & History", () => s.Step = 2, s.InvalidateLayout, BuildCalling(s)));
        col.Add(pair);

        // Final abilities
        col.Add(BuildBlock(atlas, "Final Abilities", () => s.Step = 4, s.InvalidateLayout, new StatStrip(s, atlas)));

        // Skills — HoverableChip so the player can re-read each skill's
        // codex flavour text without bouncing back to the Skills folio.
        var skillsBlock = new WrapRow();
        if (s.Background is not null)
            foreach (var sk in s.Background.SkillProficiencies)
                skillsBlock.Add(new HoverableChip(atlas, popover,
                    CodexCopy.SkillName(sk),
                    CodexCopy.SkillName(sk),
                    CodexCopy.SkillDescription(sk),
                    "BACKGROUND",
                    ChipKind.SkillFromBg));
        foreach (var sk in s.ChosenSkills.OrderBy(x => x.ToString()))
        {
            string id = CodexCopy.SkillIdToJson(sk);
            skillsBlock.Add(new HoverableChip(atlas, popover,
                CodexCopy.SkillName(id),
                CodexCopy.SkillName(id),
                CodexCopy.SkillDescription(id),
                "CLASS",
                ChipKind.SkillFromClass));
        }
        col.Add(BuildBlock(atlas, "Skills", () => s.Step = 5, s.InvalidateLayout, skillsBlock));

        // Starting kit — each chip resolves the item def for description /
        // properties / damage etc. and shows them on hover.
        var kitBlock = new WrapRow();
        if (s.Class?.StartingKit is not null)
            foreach (var entry in s.Class.StartingKit)
                kitBlock.Add(new KitItemWidget(atlas, popover, entry, s.Content));
        col.Add(BuildBlock(atlas, "Starting Kit", () => s.Step = 2, s.InvalidateLayout, kitBlock));
        return col;
    }

    private static CodexWidget BuildLineage(CodexCharacterCreationScreen s)
    {
        var col = new Column { Spacing = 4 };
        col.Add(new CodexLabel(s.Clade?.Name ?? "—", CodexFonts.DisplayMedium, CodexColors.Ink));
        col.Add(new CodexLabel(
            (s.Species?.Name ?? "—") + (s.Species is not null ? $"  ·  {CodexCopy.SizeLabel(s.Species.Size).ToUpperInvariant()}" : ""),
            CodexFonts.SerifBody, CodexColors.InkSoft));
        return col;
    }

    private static CodexWidget BuildCalling(CodexCharacterCreationScreen s)
    {
        var col = new Column { Spacing = 4 };
        col.Add(new CodexLabel(s.Class?.Name ?? "—", CodexFonts.DisplayMedium, CodexColors.Ink));
        col.Add(new CodexLabel(
            (s.Class is not null ? $"D{s.Class.HitDie}  ·  {string.Join("/", s.Class.PrimaryAbility)}" : ""),
            CodexFonts.MonoTagSmall, CodexColors.InkMute));
        col.Add(new CodexLabel(s.Background?.Name ?? "—", CodexFonts.SerifItalic, CodexColors.InkSoft));
        return col;
    }

    private static CodexWidget BuildBlock(CodexAtlas atlas, string title, System.Action onEdit, System.Action onAfterEdit, CodexWidget body)
    {
        var inner = new Column { Spacing = 8 };
        var head = new Row { Spacing = 8, VAlignChildren = VAlign.Middle };
        head.Add(new CodexLabel(title, CodexFonts.DisplaySmall, CodexColors.Ink));
        var spacerL = new HSpacer();
        head.Add(spacerL);
        head.Add(new EditLink(atlas, () => { onEdit(); onAfterEdit(); }));
        inner.Add(head);
        inner.Add(body);
        return new CodexPanel(atlas, inner);
    }
}

internal sealed class EditLink : CodexWidget
{
    private readonly CodexAtlas _atlas;
    private readonly FontStashSharp.SpriteFontBase _font = CodexFonts.MonoTagSmall;
    private readonly System.Action _onClick;
    private bool _hovered;
    private const string Text = "EDIT ›";

    public EditLink(CodexAtlas atlas, System.Action onClick) { _atlas = atlas; _onClick = onClick; }

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

    public override void Update(GameTime gt, CodexInput input)
    {
        _hovered = ContainsPoint(input.MousePosition);
        if (_hovered && input.LeftJustReleased) _onClick();
    }

    public override void Draw(SpriteBatch sb, GameTime gt)
    {
        var s = _font.MeasureString(Text);
        var color = _hovered ? CodexColors.GildBright : CodexColors.Gild;
        _font.DrawText(sb, Text, new Vector2(Bounds.X, Bounds.Y + (Bounds.Height - _font.LineHeight) / 2f), color);
    }
}

internal sealed class HSpacer : CodexWidget
{
    protected override Point MeasureCore(Point available) => new(System.Math.Max(0, available.X - 80), 1);
    protected override void ArrangeCore(Rectangle bounds) { }
}

internal sealed class KitItemWidget : CodexWidget
{
    private readonly CodexAtlas _atlas;
    private readonly CodexHoverPopover _popover;
    private readonly Theriapolis.Core.Data.StartingKitItem _entry;
    private readonly Theriapolis.Core.Data.ItemDef? _itemDef;
    private readonly FontStashSharp.SpriteFontBase _font = CodexFonts.SerifBody;
    private readonly FontStashSharp.SpriteFontBase _qtyFont = CodexFonts.MonoTagSmall;
    private bool _hovered;

    public KitItemWidget(CodexAtlas atlas, CodexHoverPopover popover,
                         Theriapolis.Core.Data.StartingKitItem entry,
                         Theriapolis.Core.Data.ContentResolver content)
    {
        _atlas = atlas;
        _popover = popover;
        _entry = entry;
        // Item id may not resolve (forward-compat starting kits, missing
        // entries) — gracefully fall back to a name-only popover.
        content.Items.TryGetValue(entry.ItemId, out _itemDef);
    }

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

    public override void Update(GameTime gt, CodexInput input)
    {
        _hovered = ContainsPoint(input.MousePosition);
        if (_hovered) ShowPopover();
    }

    private void ShowPopover()
    {
        string title = CodexCopy.ItemName(_entry.ItemId);
        string body  = ComposePopoverBody();
        string? tag  = _itemDef?.Kind?.ToUpperInvariant();
        _popover.Show(Bounds, title, body, tag);
    }

    /// <summary>Compose a multi-line description from the item's stats and
    /// the auto-equip slot. Falls back to the codex flavor description
    /// when no kind-specific stats are available.</summary>
    private string ComposePopoverBody()
    {
        var sb = new System.Text.StringBuilder();
        if (_entry.Qty > 1) sb.Append($"Quantity: ×{_entry.Qty}\n");
        if (_entry.AutoEquip) sb.Append($"Equipped to: {_entry.EquipSlot.Replace('_', ' ')}\n");

        if (_itemDef is not null)
        {
            switch (_itemDef.Kind)
            {
                case "weapon":
                    sb.Append($"Damage: {_itemDef.Damage}");
                    if (!string.IsNullOrEmpty(_itemDef.DamageType)) sb.Append(' ').Append(_itemDef.DamageType);
                    if (!string.IsNullOrEmpty(_itemDef.DamageVersatile))
                        sb.Append($"  ({_itemDef.DamageVersatile} two-handed)");
                    sb.Append('\n');
                    break;
                case "armor":
                    sb.Append($"AC {_itemDef.AcBase}");
                    if (_itemDef.AcMaxDex >= 0) sb.Append($" + DEX (max +{_itemDef.AcMaxDex})");
                    if (_itemDef.MinStr > 0) sb.Append($"  ·  Min STR {_itemDef.MinStr}");
                    sb.Append('\n');
                    break;
                case "shield":
                    sb.Append($"+{_itemDef.AcBase} AC\n");
                    break;
                case "consumable":
                    if (!string.IsNullOrEmpty(_itemDef.Healing)) sb.Append($"Heals {_itemDef.Healing}\n");
                    break;
            }
            if (_itemDef.Properties.Length > 0)
                sb.Append("Properties: ").Append(string.Join(", ", _itemDef.Properties)).Append('\n');
            if (!string.IsNullOrEmpty(_itemDef.Description))
                sb.Append(_itemDef.Description);
        }
        if (sb.Length == 0) sb.Append("(no description)");
        return sb.ToString().TrimEnd('\n');
    }

    public override void Draw(SpriteBatch sb, GameTime gt)
    {
        Color border = _hovered ? CodexColors.Gild
                     : _entry.AutoEquip ? CodexColors.Gild
                     : CodexColors.Rule;
        sb.Draw(_atlas.Pixel, Bounds, CodexColors.Bg);
        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);

        _font.DrawText(sb, CodexCopy.ItemName(_entry.ItemId),
                       new Vector2(Bounds.X + 8, Bounds.Y + 6), CodexColors.Ink);
        _qtyFont.DrawText(sb, "×" + _entry.Qty,
                          new Vector2(Bounds.X + 8, Bounds.Y + 22), CodexColors.InkMute);
        if (_entry.AutoEquip)
            _qtyFont.DrawText(sb, _entry.EquipSlot.ToUpperInvariant(),
                              new Vector2(Bounds.X + 8, Bounds.Bottom - _qtyFont.LineHeight - 4),
                              CodexColors.Gild);
    }
}
