using FontStashSharp;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Theriapolis.Core.Rules.Stats;
using Theriapolis.Game.CodexUI.Core;
using Theriapolis.Game.CodexUI.Steps;
using Theriapolis.Game.CodexUI.Widgets;
using Theriapolis.Game.UI;

namespace Theriapolis.Game.CodexUI.Screens;

/// <summary>
/// Right-column live summary. Reads from <see cref="CodexCharacterCreationScreen"/>'s
/// state and renders five blocks (Name, Lineage, Calling+History, Abilities,
/// Skills) plus a stat strip. Mirrors the React <c>&lt;Aside /&gt;</c>
/// component in <c>app.jsx</c>.
/// </summary>
public sealed class CodexAside
{
    private readonly CodexCharacterCreationScreen _s;
    private readonly CodexAtlas _atlas;

    public CodexAside(CodexCharacterCreationScreen s, CodexAtlas atlas)
    {
        _s = s;
        _atlas = atlas;
    }

    public CodexWidget Build()
    {
        // Aside lives next to the page main and shares the screen's
        // popover layer — hovering a chip here pops the same parchment-
        // and-gilt popover the cards on the left side use.
        var popover = _s.AsidePopover;

        var col = new Column { Spacing = 14, HAlignChildren = HAlign.Stretch };
        col.Add(new CodexLabel("THE SUBJECT", CodexFonts.MonoTag, CodexColors.InkMute));

        col.Add(NameBlock());
        col.Add(LineageBlock(popover));
        col.Add(CallingBlock(popover));
        col.Add(HistoryBlock(popover));
        col.Add(BuildStatStrip());
        col.Add(SkillsBlock(popover));
        return col;
    }

    private CodexWidget NameBlock()
    {
        var col = new Column { Spacing = 4 };
        col.Add(new CodexLabel("NAME", CodexFonts.MonoTag, CodexColors.InkMute));
        col.Add(new CodexLabel(string.IsNullOrWhiteSpace(_s.Name) ? "(unnamed)" : _s.Name,
                               CodexFonts.DisplayMedium, CodexColors.Ink));
        return col;
    }

    private CodexWidget LineageBlock(CodexHoverPopover popover)
    {
        var col = new Column { Spacing = 4 };
        col.Add(new CodexLabel("LINEAGE", CodexFonts.MonoTag, CodexColors.InkMute));
        col.Add(new CodexLabel(_s.Species?.Name ?? "—",
                               CodexFonts.DisplayMedium, CodexColors.Ink));
        if (_s.Clade is not null || _s.Species is not null)
        {
            string sub = (_s.Clade?.Name ?? "—").ToUpperInvariant()
                       + (_s.Clade is not null ? "  · " + _s.Clade.Kind.ToUpperInvariant() : "")
                       + (_s.Species is not null ? "  · " + CodexCopy.SizeLabel(_s.Species.Size).ToUpperInvariant() : "");
            col.Add(new CodexLabel(sub, CodexFonts.MonoTagSmall, CodexColors.InkMute));
        }

        // Trait chips — clade traits + species traits (each hover-popover'd).
        var chips = new WrapRow();
        if (_s.Clade is not null)
        {
            foreach (var t in _s.Clade.Traits)
                chips.Add(new HoverableChip(_atlas, popover, t.Name, t.Name, t.Description, null, ChipKind.Trait));
            foreach (var t in _s.Clade.Detriments)
                chips.Add(new HoverableChip(_atlas, popover, t.Name, t.Name, t.Description, "DETRIMENT", ChipKind.TraitDetriment));
        }
        if (_s.Species is not null)
        {
            foreach (var t in _s.Species.Traits)
                chips.Add(new HoverableChip(_atlas, popover, t.Name, t.Name, t.Description, null, ChipKind.Trait));
            foreach (var t in _s.Species.Detriments)
                chips.Add(new HoverableChip(_atlas, popover, t.Name, t.Name, t.Description, "DETRIMENT", ChipKind.TraitDetriment));
        }
        if (chips.Children.Count > 0) col.Add(chips);
        return col;
    }

    private CodexWidget CallingBlock(CodexHoverPopover popover)
    {
        var col = new Column { Spacing = 4 };
        col.Add(new CodexLabel("CALLING", CodexFonts.MonoTag, CodexColors.InkMute));
        col.Add(new CodexLabel(_s.Class?.Name ?? "—", CodexFonts.DisplayMedium, CodexColors.Ink));
        if (_s.Class is not null)
            col.Add(new CodexLabel($"D{_s.Class.HitDie}  ·  {string.Join("/", _s.Class.PrimaryAbility)}",
                                   CodexFonts.MonoTagSmall, CodexColors.InkMute));

        // Level-1 feature chips for the chosen class (hover surfaces description).
        if (_s.Class is not null)
        {
            var lvl1 = System.Array.Find(_s.Class.LevelTable, e => e.Level == 1);
            if (lvl1 is not null)
            {
                var chips = new WrapRow();
                foreach (var k in lvl1.Features)
                {
                    if (k == "asi" || k == "subclass_select" || k == "subclass_feature") continue;
                    if (!_s.Class.FeatureDefinitions.TryGetValue(k, out var fd)) continue;
                    chips.Add(new HoverableChip(_atlas, popover, fd.Name, fd.Name, fd.Description, fd.Kind?.ToUpperInvariant(), ChipKind.Trait));
                }
                if (chips.Children.Count > 0) col.Add(chips);
            }
        }
        return col;
    }

    private CodexWidget HistoryBlock(CodexHoverPopover popover)
    {
        var col = new Column { Spacing = 4 };
        col.Add(new CodexLabel("HISTORY", CodexFonts.MonoTag, CodexColors.InkMute));
        col.Add(new CodexLabel(_s.Background?.Name ?? "—", CodexFonts.DisplayMedium, CodexColors.Ink));
        if (_s.Background is not null && !string.IsNullOrEmpty(_s.Background.FeatureName))
        {
            var chips = new WrapRow();
            chips.Add(new HoverableChip(_atlas, popover,
                _s.Background.FeatureName, _s.Background.FeatureName, _s.Background.FeatureDescription,
                "FEATURE", ChipKind.BgFeature));
            col.Add(chips);
        }
        return col;
    }

    private CodexWidget SkillsBlock(CodexHoverPopover popover)
    {
        int total = _s.ChosenSkills.Count + (_s.Background?.SkillProficiencies.Length ?? 0);
        var col = new Column { Spacing = 4 };
        col.Add(new CodexLabel("SKILLS · " + total, CodexFonts.MonoTag, CodexColors.InkMute));
        var chips = new WrapRow();
        if (_s.Background is not null)
            foreach (var s in _s.Background.SkillProficiencies)
                chips.Add(new HoverableChip(_atlas, popover,
                    CodexCopy.SkillName(s),
                    CodexCopy.SkillName(s),
                    CodexCopy.SkillDescription(s),
                    "BACKGROUND",
                    ChipKind.SkillFromBg));
        foreach (var s in _s.ChosenSkills.OrderBy(x => x.ToString()))
        {
            // Map enum → snake_case JSON id; otherwise SleightOfHand /
            // AnimalHandling lose their hover descriptions because the
            // CodexCopy switch is keyed on the JSON form.
            string id = CodexCopy.SkillIdToJson(s);
            chips.Add(new HoverableChip(_atlas, popover,
                CodexCopy.SkillName(id),
                CodexCopy.SkillName(id),
                CodexCopy.SkillDescription(id),
                "CLASS",
                ChipKind.SkillFromClass));
        }
        col.Add(chips);
        return col;
    }

    private CodexWidget BuildStatStrip()
    {
        var col = new Column { Spacing = 4 };
        col.Add(new CodexLabel("ABILITIES", CodexFonts.MonoTag, CodexColors.InkMute));
        col.Add(new StatStrip(_s, _atlas));
        return col;
    }
}

/// <summary>
/// Six-cell strip (one per ability) with name, final score, and signed
/// modifier. Used in the aside panel and on the review step.
/// </summary>
internal sealed class StatStrip : CodexWidget
{
    private readonly CodexCharacterCreationScreen _s;
    private readonly CodexAtlas _atlas;

    public StatStrip(CodexCharacterCreationScreen s, CodexAtlas atlas) { _s = s; _atlas = atlas; }

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

    public override void Draw(SpriteBatch sb, GameTime gt)
    {
        var abFont = CodexFonts.MonoTagSmall;
        var scoreFont = CodexFonts.DisplayMedium;
        var modFont = CodexFonts.MonoTagSmall;
        int cellW = (Bounds.Width - 5 * 4) / 6;
        for (int i = 0; i < CodexCopy.AbilityOrder.Length; i++)
        {
            var ab = CodexCopy.AbilityOrder[i];
            int x = Bounds.X + i * (cellW + 4);
            var rect = new Rectangle(x, Bounds.Y, cellW, Bounds.Height);
            sb.Draw(_atlas.Pixel, rect, CodexColors.Bg);
            DrawBorder(sb, rect, CodexColors.Rule, 1);

            string ablab = ab.ToString();
            var s1 = abFont.MeasureString(ablab);
            abFont.DrawText(sb, ablab, new Vector2(rect.X + (rect.Width - s1.X) / 2f, rect.Y + 4), CodexColors.InkMute);

            int? base_ = _s.StatAssign.TryGetValue(ab, out var v) ? v : (int?)null;
            int total = (base_ ?? 0) + _s.TotalBonus(ab);
            int mod = AbilityScores.Mod(total);
            string scoreText = base_ is null ? "—" : total.ToString();
            string modText = base_ is null ? "" : ((mod >= 0 ? "+" : "") + mod);

            var s2 = scoreFont.MeasureString(scoreText);
            scoreFont.DrawText(sb, scoreText, new Vector2(rect.X + (rect.Width - s2.X) / 2f, rect.Y + 16), CodexColors.Ink);
            if (modText.Length > 0)
            {
                var s3 = modFont.MeasureString(modText);
                modFont.DrawText(sb, modText, new Vector2(rect.X + (rect.Width - s3.X) / 2f, rect.Bottom - modFont.LineHeight - 4),
                                 mod >= 0 ? CodexColors.Seal : 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);
    }
}
