using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Myra.Graphics2D;
using Myra.Graphics2D.Brushes;
using Myra.Graphics2D.UI;
using Theriapolis.Core;
using Theriapolis.Core.Rules.Character;
using Theriapolis.Core.Rules.Stats;

namespace Theriapolis.Game.Screens;

/// <summary>
/// Phase 6.5 M0 — the level-up modal. Pushed by <see cref="PauseMenuScreen"/>
/// when the player clicks "Level Up" while
/// <see cref="LevelUpFlow.CanLevelUp"/> returns true.
///
/// Shows the rolled (or averaged) HP gain, the feature unlocks for this
/// level, and — when applicable — the ASI picker and subclass picker. On
/// confirm, applies the deltas to the player's <see cref="Character"/>
/// via <see cref="Character.ApplyLevelUp"/> and pops; if the player still
/// has enough XP for another level, the screen offers to re-open.
/// </summary>
public sealed class LevelUpScreen : IScreen
{
    private readonly Character _character;
    private readonly ulong _baseSeed;
    private readonly IReadOnlyDictionary<string, Theriapolis.Core.Data.SubclassDef>? _subclasses;
    private Game1 _game = null!;
    private Desktop _desktop = null!;
    private LevelUpResult _preview = null!;
    private LevelUpChoices _choices = null!;
    private Label? _statusLabel;
    private bool _escWasDown = true;

    public LevelUpScreen(
        Character character,
        ulong baseSeed,
        IReadOnlyDictionary<string, Theriapolis.Core.Data.SubclassDef>? subclasses = null)
    {
        _character  = character ?? throw new ArgumentNullException(nameof(character));
        _baseSeed   = baseSeed;
        _subclasses = subclasses;
    }

    public void Initialize(Game1 game)
    {
        _game = game;
        RecomputePreview(takeAverage: true);
        Build();
    }

    private void RecomputePreview(bool takeAverage)
    {
        int targetLevel = _character.Level + 1;
        ulong seed = _baseSeed
                   ^ C.RNG_LEVELUP
                   ^ (ulong)targetLevel
                   // Mix in level-up history length so each successive level-up
                   // (when the player chains multiple at once) gets a distinct
                   // sub-seed even when targetLevel is reused after re-entry.
                   ^ ((ulong)_character.LevelUpHistory.Count << 16);
        _preview = LevelUpFlow.Compute(_character, targetLevel, seed,
                                        takeAverage: takeAverage,
                                        subclasses: _subclasses);
        _choices = new LevelUpChoices { TakeAverageHp = takeAverage };
    }

    private void Build()
    {
        var root = new VerticalStackPanel
        {
            Spacing             = 8,
            HorizontalAlignment = HorizontalAlignment.Center,
            VerticalAlignment   = VerticalAlignment.Center,
            Background          = new SolidBrush(new Color(0, 0, 0, 220)),
            Padding             = new Thickness(40, 24, 40, 24),
        };

        root.Widgets.Add(new Label
        {
            Text = $"LEVEL UP — Level {_character.Level} → {_preview.NewLevel}",
            HorizontalAlignment = HorizontalAlignment.Center,
        });
        root.Widgets.Add(new Label { Text = " " });

        // HP section.
        string hpLine = _preview.HpWasAveraged
            ? $"HP: +{_preview.HpGained}  (took average; rolled would be 1d{_character.ClassDef.HitDie})"
            : $"HP: +{_preview.HpGained}  (rolled {_preview.HpHitDieResult} on 1d{_character.ClassDef.HitDie})";
        root.Widgets.Add(new Label { Text = hpLine, HorizontalAlignment = HorizontalAlignment.Center });

        var hpToggle = new TextButton
        {
            Text = _preview.HpWasAveraged ? "Switch to: Roll HP" : "Switch to: Take average HP",
            Width = 280,
            HorizontalAlignment = HorizontalAlignment.Center,
        };
        hpToggle.Click += (_, _) =>
        {
            RecomputePreview(takeAverage: !_preview.HpWasAveraged);
            Build();
        };
        root.Widgets.Add(hpToggle);

        // Class features.
        if (_preview.ClassFeaturesUnlocked.Length > 0)
        {
            root.Widgets.Add(new Label { Text = " " });
            root.Widgets.Add(new Label { Text = "Features unlocked:", HorizontalAlignment = HorizontalAlignment.Center });
            foreach (var fid in _preview.ClassFeaturesUnlocked)
            {
                string display = fid;
                if (_character.ClassDef.FeatureDefinitions.TryGetValue(fid, out var def))
                    display = string.IsNullOrEmpty(def.Name) ? fid : $"{def.Name} — {def.Kind}";
                root.Widgets.Add(new Label { Text = "  • " + display, HorizontalAlignment = HorizontalAlignment.Center });
            }
        }

        // Phase 6.5 M2 — subclass features (post-L3, when SubclassId is set).
        if (_preview.SubclassFeaturesUnlocked.Length > 0 && _subclasses is not null)
        {
            var subclass = Theriapolis.Core.Rules.Character.SubclassResolver.TryFindSubclass(
                _subclasses, _character.SubclassId);
            string subclassName = subclass?.Name ?? _character.SubclassId;
            root.Widgets.Add(new Label { Text = " " });
            root.Widgets.Add(new Label
            {
                Text = $"{subclassName} features:",
                HorizontalAlignment = HorizontalAlignment.Center,
                TextColor = new Color(220, 200, 140),
            });
            foreach (var fid in _preview.SubclassFeaturesUnlocked)
            {
                string display = fid;
                var fdef = Theriapolis.Core.Rules.Character.SubclassResolver.ResolveFeatureDef(
                    _character.ClassDef, subclass, fid);
                if (fdef is not null)
                    display = string.IsNullOrEmpty(fdef.Name) ? fid : $"{fdef.Name} — {fdef.Kind}";
                root.Widgets.Add(new Label { Text = "  • " + display, HorizontalAlignment = HorizontalAlignment.Center });
            }
        }

        root.Widgets.Add(new Label { Text = " " });
        root.Widgets.Add(new Label
        {
            Text = $"Proficiency bonus: +{_preview.NewProficiencyBonus}",
            HorizontalAlignment = HorizontalAlignment.Center,
        });

        // Subclass picker.
        if (_preview.GrantsSubclassChoice)
        {
            root.Widgets.Add(new Label { Text = " " });
            root.Widgets.Add(new Label { Text = "Choose a subclass:", HorizontalAlignment = HorizontalAlignment.Center });
            foreach (var sid in _character.ClassDef.SubclassIds)
            {
                string sCapture = sid;
                string label = sid;
                string? flavor = null;
                if (_subclasses is not null
                    && _subclasses.TryGetValue(sid, out var subDef))
                {
                    label = subDef.Name;
                    flavor = subDef.Flavor;
                }
                var btn = new TextButton
                {
                    Text = $"  {label}{(_choices.SubclassId == sCapture ? "  ✓" : "")}",
                    Width = 360,
                    HorizontalAlignment = HorizontalAlignment.Center,
                };
                btn.Click += (_, _) =>
                {
                    _choices.SubclassId = sCapture;
                    Build();
                };
                root.Widgets.Add(btn);
                if (_choices.SubclassId == sCapture && !string.IsNullOrEmpty(flavor))
                {
                    root.Widgets.Add(new Label
                    {
                        Text = "    " + flavor,
                        Wrap = true,
                        Width = 600,
                        HorizontalAlignment = HorizontalAlignment.Center,
                        TextColor = new Color(170, 170, 170),
                    });
                }
            }
        }

        // ASI picker.
        if (_preview.GrantsAsiChoice)
        {
            root.Widgets.Add(new Label { Text = " " });
            root.Widgets.Add(new Label { Text = "Ability Score Improvement (+2 to one or +1 to two):", HorizontalAlignment = HorizontalAlignment.Center });
            int allocated = _choices.AsiAdjustments.Values.Sum();
            root.Widgets.Add(new Label
            {
                Text = $"Allocated: +{allocated} / +2",
                HorizontalAlignment = HorizontalAlignment.Center,
            });
            foreach (AbilityId aid in Enum.GetValues<AbilityId>())
            {
                var aidCapture = aid;
                int current = _character.Abilities.Get(aid);
                int delta = _choices.AsiAdjustments.TryGetValue(aid, out var d) ? d : 0;
                var row = new HorizontalStackPanel
                {
                    Spacing = 4,
                    HorizontalAlignment = HorizontalAlignment.Center,
                };
                row.Widgets.Add(new Label { Text = $"  {aid}: {current}{(delta > 0 ? $" → {current + delta}" : "")}    ", VerticalAlignment = VerticalAlignment.Center });
                var minus = new TextButton { Text = "−", Width = 30 };
                minus.Click += (_, _) =>
                {
                    if (_choices.AsiAdjustments.TryGetValue(aidCapture, out var v) && v > 0)
                    {
                        if (v == 1) _choices.AsiAdjustments.Remove(aidCapture);
                        else _choices.AsiAdjustments[aidCapture] = v - 1;
                        Build();
                    }
                };
                var plus = new TextButton { Text = "+", Width = 30 };
                plus.Click += (_, _) =>
                {
                    int totalAllocated = _choices.AsiAdjustments.Values.Sum();
                    if (totalAllocated >= 2) return;  // cap at +2
                    int currentDelta = _choices.AsiAdjustments.TryGetValue(aidCapture, out var v) ? v : 0;
                    if (currentDelta >= 2) return;
                    int currentScore = _character.Abilities.Get(aidCapture);
                    if (currentScore + currentDelta + 1 > C.ABILITY_SCORE_CAP_PRE_L20) return;
                    _choices.AsiAdjustments[aidCapture] = currentDelta + 1;
                    Build();
                };
                row.Widgets.Add(minus);
                row.Widgets.Add(plus);
                root.Widgets.Add(row);
            }
        }

        root.Widgets.Add(new Label { Text = " " });

        // Confirm button.
        bool valid = ChoicesValid(out string reason);
        var confirm = new TextButton
        {
            Text  = valid ? "Confirm" : $"Confirm — {reason}",
            Width = 280,
            HorizontalAlignment = HorizontalAlignment.Center,
            Enabled = valid,
        };
        confirm.Click += (_, _) =>
        {
            if (!ChoicesValid(out _)) return;
            _character.ApplyLevelUp(_preview, _choices);
            // Chain into the next level-up immediately if eligible.
            if (LevelUpFlow.CanLevelUp(_character))
            {
                RecomputePreview(takeAverage: true);
                Build();
                ShowStatus($"Now level {_character.Level}. Another level available!");
            }
            else
            {
                _game.Screens.Pop();
            }
        };
        root.Widgets.Add(confirm);

        var cancel = new TextButton { Text = "Cancel", Width = 280, HorizontalAlignment = HorizontalAlignment.Center };
        cancel.Click += (_, _) => _game.Screens.Pop();
        root.Widgets.Add(cancel);

        _statusLabel = new Label { Text = " ", HorizontalAlignment = HorizontalAlignment.Center };
        root.Widgets.Add(_statusLabel);

        _desktop = new Desktop { Root = root };
    }

    private bool ChoicesValid(out string reason)
    {
        if (_preview.GrantsSubclassChoice && string.IsNullOrEmpty(_choices.SubclassId))
        {
            reason = "pick a subclass";
            return false;
        }
        if (_preview.GrantsAsiChoice)
        {
            int total = _choices.AsiAdjustments.Values.Sum();
            if (total != 2)
            {
                reason = $"allocate +{2 - total} more ASI";
                return false;
            }
        }
        reason = "";
        return true;
    }

    private void ShowStatus(string text)
    {
        if (_statusLabel is not null) _statusLabel.Text = text;
    }

    public void Update(GameTime gt)
    {
        bool down = Keyboard.GetState().IsKeyDown(Keys.Escape);
        bool justPressed = down && !_escWasDown;
        _escWasDown = down;
        if (justPressed) _game.Screens.Pop();
    }

    public void Draw(GameTime gt, SpriteBatch sb)
    {
        _desktop.Render();
    }

    public void Deactivate() { }
    public void Reactivate() { Build(); }
}
