using Godot;
using System.Collections.Generic;
using Theriapolis.GodotHost.Scenes.Widgets;
using Theriapolis.GodotHost.UI;

namespace Theriapolis.GodotHost.Scenes;

/// <summary>
/// Right-rail summary of the in-progress character. Sections, top-down:
///   1. Name (or placeholder until Step VIII).
///   2. Lineage details — 2-column grid:
///        purebred:   Clade | Species, then Calling | Background.
///        hybrid:     Sire | Dam (column headers); each parent's
///                    clade and species below; Calling | Background.
///   3. Attributes — final score + d20 modifier per ability.
///   4. Pills — traits + skills selected so far, with hover popovers.
///
/// One Refresh() rebuild per draft change; the panel is small enough
/// that partial-update logic isn't worth the complexity.
/// </summary>
public partial class Aside : MarginContainer
{
    private CharacterDraft? _draft;
    private VBoxContainer _content = null!;

    public override void _Ready()
    {
        AddThemeConstantOverride("margin_left", 18);
        AddThemeConstantOverride("margin_right", 18);
        AddThemeConstantOverride("margin_top", 18);
        AddThemeConstantOverride("margin_bottom", 18);

        // Wrap content in a ScrollContainer so the Aside's intrinsic
        // height stays bounded by the panel's allocated size — without
        // this, an over-tall summary (lots of pills) forces the parent
        // Layout to expand and pushes the navbar off the viewport.
        var scroll = new ScrollContainer
        {
            SizeFlagsHorizontal = SizeFlags.ExpandFill,
            SizeFlagsVertical   = SizeFlags.ExpandFill,
            HorizontalScrollMode = ScrollContainer.ScrollMode.Disabled,
        };
        AddChild(scroll);

        _content = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
        _content.AddThemeConstantOverride("separation", 18);
        scroll.AddChild(_content);
    }

    public void SetDraft(CharacterDraft draft)
    {
        _draft = draft;
        _draft.Changed += Refresh;
        Refresh();
    }

    private void Refresh()
    {
        if (_draft is null || _content is null) return;
        foreach (var c in _content.GetChildren()) c.QueueFree();

        BuildName();
        BuildDetailsGrid();
        BuildAttributes();
        BuildPills();
    }

    // ──────────────────────────────────────────────────────────────────────
    // Section 1 — Name

    private void BuildName()
    {
        var name = string.IsNullOrEmpty(_draft!.CharacterName) ? "Unnamed" : _draft.CharacterName;
        _content.AddChild(new Label
        {
            Text = name,
            HorizontalAlignment = HorizontalAlignment.Center,
        });
        _content.AddChild(new HSeparator());
    }

    // ──────────────────────────────────────────────────────────────────────
    // Section 2 — Lineage details (2-column grid)

    private void BuildDetailsGrid()
    {
        if (_draft!.IsHybrid)
        {
            // Hybrid layout: SIRE / DAM column headers above the parent
            // detail rows, then the Calling / Background row spans both
            // halves of the same kind of grid.
            var headers = new HBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
            headers.AddThemeConstantOverride("separation", 12);
            _content.AddChild(headers);
            headers.AddChild(MakeColumnHeader("SIRE" + (_draft.DominantParent == "sire" ? " ★" : "")));
            headers.AddChild(MakeColumnHeader("DAM"  + (_draft.DominantParent == "dam"  ? " ★" : "")));

            var lineageGrid = MakeFullWidthGrid();
            _content.AddChild(lineageGrid);
            lineageGrid.AddChild(MakeCell("Clade",   CodexContent.Clade(_draft.SireCladeId)?.Name));
            lineageGrid.AddChild(MakeCell("Clade",   CodexContent.Clade(_draft.DamCladeId)?.Name));
            lineageGrid.AddChild(MakeCell("Species", CodexContent.SpeciesById(_draft.SireSpeciesId)?.Name));
            lineageGrid.AddChild(MakeCell("Species", CodexContent.SpeciesById(_draft.DamSpeciesId)?.Name));
        }
        else
        {
            var lineageGrid = MakeFullWidthGrid();
            _content.AddChild(lineageGrid);
            lineageGrid.AddChild(MakeCell("Clade",   CodexContent.Clade(_draft.CladeId)?.Name));
            lineageGrid.AddChild(MakeCell("Species", CodexContent.SpeciesById(_draft.SpeciesId)?.Name));
        }

        // Calling + Background — last row of the lineage block, with
        // Subclass tucked underneath Calling in the same column.
        var callingGrid = MakeFullWidthGrid();
        _content.AddChild(callingGrid);
        callingGrid.AddChild(MakeCell("Calling",    CodexContent.Class(_draft.ClassId)?.Name));
        callingGrid.AddChild(MakeCell("Background", CodexContent.Background(_draft.BackgroundId)?.Name));

        var subclassDef = System.Array.Find(CodexContent.Subclasses, s => s.Id == _draft.SubclassId);
        callingGrid.AddChild(MakeCell("Subclass", subclassDef?.Name));
        callingGrid.AddChild(new Control());  // empty cell to align grid

        _content.AddChild(new HSeparator());
    }

    private static GridContainer MakeFullWidthGrid()
    {
        var grid = new GridContainer { Columns = 2, SizeFlagsHorizontal = SizeFlags.ExpandFill };
        grid.AddThemeConstantOverride("h_separation", 12);
        grid.AddThemeConstantOverride("v_separation", 8);
        return grid;
    }

    private static Control MakeColumnHeader(string label)
    {
        // Centered label + underline; sized to take half the parent
        // HBoxContainer width so SIRE and DAM align over their data
        // columns.
        var col = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
        col.AddThemeConstantOverride("separation", 2);
        col.AddChild(new Label
        {
            Text = label,
            HorizontalAlignment = HorizontalAlignment.Center,
        });
        col.AddChild(new HSeparator());
        return col;
    }

    private static Control MakeCell(string label, string? value)
    {
        var v = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
        v.AddThemeConstantOverride("separation", 2);

        // Smaller font on the label tag — keeps the row compact in the
        // narrow side rail.
        var lbl = new Label { Text = label.ToUpperInvariant() };
        lbl.AddThemeFontSizeOverride("font_size", 11);
        v.AddChild(lbl);

        // Autowrap on the value so long names ("Hybrid Underground")
        // wrap rather than push the whole panel wider than its alloc.
        var val = new Label
        {
            Text = string.IsNullOrEmpty(value) ? "—" : value,
            AutowrapMode = TextServer.AutowrapMode.WordSmart,
            CustomMinimumSize = new Vector2(0, 0),
        };
        v.AddChild(val);
        return v;
    }

    // ──────────────────────────────────────────────────────────────────────
    // Section 3 — Attributes (final score + modifier)

    private void BuildAttributes()
    {
        _content.AddChild(new Label { Text = "ATTRIBUTES" });

        // Self-contained sub-panel so the attributes table never widens
        // beyond the Aside's own rect. Columns: ab | bonus | final | d20.
        // Cells have explicit min widths but DON'T ExpandFill, so the
        // grid's total width is bounded by the cell mins instead of
        // taking whatever parent width is available.
        var grid = new GridContainer { Columns = 4 };
        grid.AddThemeConstantOverride("h_separation", 6);
        grid.AddThemeConstantOverride("v_separation", 6);
        _content.AddChild(grid);

        foreach (var ab in SkillsCatalog.Abilities)
        {
            int baseScore = AbilityCalc.BaseScore(ab, _draft!);
            int bonus     = AbilityCalc.TotalBonus(ab, _draft!);
            int final     = baseScore + bonus;
            int dMod      = AbilityCalc.D20Modifier(final);

            grid.AddChild(new Label
            {
                Text = ab,
                CustomMinimumSize = new Vector2(36, 0),
            });

            // Bonus chip — only render when non-zero. +0 entries get an
            // empty Control so the column stays aligned without the
            // panel chrome of an empty TraitChip pushing the row wider.
            if (bonus != 0)
            {
                grid.AddChild(new Widgets.TraitChip
                {
                    TraitName = AbilityCalc.FormatSigned(bonus),
                    Description = AbilityCalc.FormatBreakdown(AbilityCalc.Sources(ab, _draft!)),
                    Tag = "bonus",
                });
            }
            else
            {
                grid.AddChild(new Control { CustomMinimumSize = new Vector2(40, 0) });
            }

            grid.AddChild(new Label
            {
                Text = baseScore == 0 ? "—" : final.ToString(),
                HorizontalAlignment = HorizontalAlignment.Right,
                CustomMinimumSize = new Vector2(36, 0),
            });

            grid.AddChild(new Label
            {
                Text = baseScore == 0 ? "" : AbilityCalc.FormatSigned(dMod),
                HorizontalAlignment = HorizontalAlignment.Right,
                CustomMinimumSize = new Vector2(36, 0),
            });
        }

        _content.AddChild(new HSeparator());
    }

    // ──────────────────────────────────────────────────────────────────────
    // Section 4 — Pills (traits, skills, features chosen so far)

    private void BuildPills()
    {
        _content.AddChild(new Label { Text = "TRAITS · FEATS · SKILLS" });

        var flow = new HFlowContainer();
        flow.AddThemeConstantOverride("h_separation", 6);
        flow.AddThemeConstantOverride("v_separation", 6);
        _content.AddChild(flow);

        // Clade traits (purebred = single clade; hybrid = both).
        if (_draft!.IsHybrid)
        {
            AddCladeTraits(flow, CodexContent.Clade(_draft.SireCladeId));
            AddCladeTraits(flow, CodexContent.Clade(_draft.DamCladeId));
        }
        else
        {
            AddCladeTraits(flow, CodexContent.Clade(_draft.CladeId));
        }

        // Species traits.
        var sp = CodexContent.SpeciesById(_draft.EffectiveSpeciesId);
        if (sp is not null)
        {
            foreach (var t in sp.Traits)
                flow.AddChild(new TraitChip { TraitName = t.Name, Description = t.Description });
            foreach (var d in sp.Detriments)
                flow.AddChild(new TraitChip { TraitName = d.Name, Description = d.Description, Detriment = true });
        }

        // Class level-1 features.
        var cls = CodexContent.Class(_draft.ClassId);
        if (cls is not null)
        {
            var lvl1 = System.Array.Find(cls.LevelTable, e => e.Level == 1);
            if (lvl1 is not null)
            {
                foreach (var fid in lvl1.Features)
                {
                    if (!cls.FeatureDefinitions.TryGetValue(fid, out var fd)) continue;
                    if (fd.Kind == "stub" || fid.StartsWith("subclass_")) continue;
                    flow.AddChild(new TraitChip
                    {
                        TraitName = fd.Name,
                        Description = fd.Description,
                        Tag = fd.Kind,
                    });
                }
            }
        }

        // Background feature + granted skills.
        var bg = CodexContent.Background(_draft.BackgroundId);
        if (bg is not null && !string.IsNullOrEmpty(bg.FeatureName))
        {
            flow.AddChild(new TraitChip
            {
                TraitName = bg.FeatureName,
                Description = bg.FeatureDescription,
                Tag = "history",
            });
        }

        // Skills — background-locked first, then user-chosen class skills.
        if (bg is not null)
        {
            foreach (var skillId in bg.SkillProficiencies)
                AddSkillChip(flow, skillId, "BG");
        }
        foreach (var skillId in _draft.ChosenSkills)
            AddSkillChip(flow, skillId, "skill");
    }

    private static void AddCladeTraits(HFlowContainer flow, Theriapolis.Core.Data.CladeDef? clade)
    {
        if (clade is null) return;
        foreach (var t in clade.Traits)
            flow.AddChild(new TraitChip { TraitName = t.Name, Description = t.Description });
        foreach (var d in clade.Detriments)
            flow.AddChild(new TraitChip { TraitName = d.Name, Description = d.Description, Detriment = true });
    }

    private static void AddSkillChip(HFlowContainer flow, string skillId, string tag)
    {
        var s = SkillsCatalog.Get(skillId);
        if (s is null) return;
        flow.AddChild(new TraitChip
        {
            TraitName = s.Label,
            Description = s.Description,
            Tag = tag,
        });
    }
}
