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.Items;
using Theriapolis.Core.Rules.Character;
using Theriapolis.Core.Rules.Stats;

namespace Theriapolis.Game.Screens;

/// <summary>
/// Phase 5 M3 inventory screen. Pushed by <see cref="PlayScreen"/> when the
/// player presses TAB. Two-column layout: equipped slots on the left, bagged
/// items on the right. Click an equipped slot to unequip; click a bagged
/// item to equip into its natural slot (weapon → main hand, armor → body,
/// shield → off hand, enhancer → matching natural-weapon slot).
///
/// All mutations are direct on the character's <see cref="Inventory"/>;
/// <see cref="Stats.DerivedStats"/> recomputes AC/Speed automatically on
/// next read, so no signals or events are needed.
/// </summary>
public sealed class InventoryScreen : IScreen
{
    private readonly Character _character;
    private Game1 _game = null!;
    private Desktop _desktop = null!;
    private Label? _statusLabel;
    private bool _tabWasDown = true;
    private bool _escWasDown = true;

    public InventoryScreen(Character character)
    {
        _character = character ?? throw new System.ArgumentNullException(nameof(character));
    }

    public void Initialize(Game1 game)
    {
        _game = game;
        BuildUI();
    }

    private void BuildUI()
    {
        var root = new VerticalStackPanel
        {
            Spacing             = 6,
            HorizontalAlignment = HorizontalAlignment.Center,
            VerticalAlignment   = VerticalAlignment.Top,
            Margin              = new Thickness(20),
            Padding             = new Thickness(20, 12, 20, 12),
            Background          = new SolidBrush(new Color(0, 0, 0, 220)),
        };

        // Header
        root.Widgets.Add(new Label
        {
            Text = $"INVENTORY — {_character.Species.Name} {_character.ClassDef.Name} (Lv{_character.Level})",
            HorizontalAlignment = HorizontalAlignment.Center,
        });
        root.Widgets.Add(new Label
        {
            Text = FormatStatLine(),
            HorizontalAlignment = HorizontalAlignment.Center,
        });
        root.Widgets.Add(new Label { Text = " " });

        // Two columns
        var columns = new HorizontalStackPanel { Spacing = 24 };

        // Equipped column
        var equippedCol = new VerticalStackPanel { Spacing = 4, Width = 320 };
        equippedCol.Widgets.Add(new Label { Text = "EQUIPPED:" });
        foreach (EquipSlot slot in EquipSlotsToShow())
            equippedCol.Widgets.Add(BuildEquippedSlotRow(slot));
        columns.Widgets.Add(equippedCol);

        // Inventory column
        var bagCol = new VerticalStackPanel { Spacing = 4, Width = 380 };
        bagCol.Widgets.Add(new Label
        {
            Text = $"INVENTORY ({_character.Inventory.Items.Count} stacks, {_character.Inventory.TotalWeightLb:F1} lb):",
        });
        bool any = false;
        foreach (var item in _character.Inventory.Items)
        {
            // Skip equipped items in the bag list — they're shown in the equipped column.
            if (item.EquippedAt is not null) continue;
            any = true;
            bagCol.Widgets.Add(BuildBagItemRow(item));
        }
        if (!any)
            bagCol.Widgets.Add(new Label { Text = "  (everything is equipped)" });
        columns.Widgets.Add(bagCol);

        root.Widgets.Add(columns);

        // Status line
        root.Widgets.Add(new Label { Text = " " });
        _statusLabel = new Label { Text = "TAB or ESC to close.", HorizontalAlignment = HorizontalAlignment.Center };
        root.Widgets.Add(_statusLabel);

        _desktop = new Desktop { Root = root };
    }

    private Widget BuildEquippedSlotRow(EquipSlot slot)
    {
        var inst = _character.Inventory.GetEquipped(slot);
        string text = inst is null
            ? $"  {SlotLabel(slot),-18}—"
            : $"  {SlotLabel(slot),-18}{inst.Def.Name}";
        var btn = new TextButton
        {
            Text = text,
            Width = 320,
            Padding = new Thickness(4, 2, 4, 2),
        };
        if (inst is not null)
            btn.Click += (_, _) =>
            {
                _character.Inventory.TryUnequip(slot, out var err);
                if (!string.IsNullOrEmpty(err) && _statusLabel is not null) _statusLabel.Text = err;
                else SetStatus($"Unequipped {inst.Def.Name}.");
                BuildUI();
            };
        else btn.Enabled = false;
        return btn;
    }

    private Widget BuildBagItemRow(ItemInstance inst)
    {
        string suffix = inst.Qty > 1 ? $" ×{inst.Qty}" : "";
        string weight = $"  ({inst.TotalWeightLb:F1} lb)";
        var btn = new TextButton
        {
            Text = $"  {inst.Def.Name}{suffix}{weight}",
            Width = 380,
            Padding = new Thickness(4, 2, 4, 2),
        };
        var auto = NaturalSlotFor(inst);
        if (auto is null)
        {
            btn.Enabled = false;  // gear / consumables — no equip target in M3
        }
        else
        {
            btn.Click += (_, _) =>
            {
                if (_character.Inventory.TryEquip(inst, auto.Value, out var err))
                    SetStatus($"Equipped {inst.Def.Name} into {auto.Value}.");
                else
                    SetStatus(err);
                BuildUI();
            };
        }
        return btn;
    }

    /// <summary>
    /// Default equip slot for an item based on kind. Returns null for items
    /// that have no obvious slot (consumables, adventuring gear).
    /// </summary>
    private static EquipSlot? NaturalSlotFor(ItemInstance inst)
    {
        switch (inst.Def.Kind)
        {
            case "weapon":
                return EquipSlot.MainHand;
            case "armor":
                return EquipSlot.Body;
            case "shield":
                return EquipSlot.OffHand;
            case "natural_weapon_enhancer":
                return EquipSlotExtensions.FromEnhancerSlot(inst.Def.EnhancerSlot);
            default:
                return null;
        }
    }

    private static IEnumerable<EquipSlot> EquipSlotsToShow() => new[]
    {
        EquipSlot.MainHand,
        EquipSlot.OffHand,
        EquipSlot.Body,
        EquipSlot.Helm,
        EquipSlot.Cloak,
        EquipSlot.Boots,
        EquipSlot.AdaptivePack,
        EquipSlot.NaturalWeaponFang,
        EquipSlot.NaturalWeaponClaw,
        EquipSlot.NaturalWeaponHoof,
        EquipSlot.NaturalWeaponAntler,
        EquipSlot.NaturalWeaponHorn,
    };

    private static string SlotLabel(EquipSlot s) => s switch
    {
        EquipSlot.MainHand              => "Main hand:",
        EquipSlot.OffHand               => "Off hand:",
        EquipSlot.Body                  => "Body:",
        EquipSlot.Helm                  => "Helm:",
        EquipSlot.Cloak                 => "Cloak:",
        EquipSlot.Boots                 => "Boots:",
        EquipSlot.AdaptivePack          => "Pack:",
        EquipSlot.NaturalWeaponFang     => "Fang caps:",
        EquipSlot.NaturalWeaponClaw     => "Claw sheaths:",
        EquipSlot.NaturalWeaponHoof     => "Hoof plates:",
        EquipSlot.NaturalWeaponAntler   => "Antler tips:",
        EquipSlot.NaturalWeaponHorn     => "Horn rings:",
        _ => s.ToString(),
    };

    private string FormatStatLine()
    {
        int ac    = DerivedStats.ArmorClass(_character);
        int spd   = DerivedStats.SpeedFt(_character);
        float cap = DerivedStats.CarryCapacityLb(_character);
        var enc   = DerivedStats.Encumbrance(_character);
        return $"HP {_character.CurrentHp}/{_character.MaxHp}   AC {ac}   Speed {spd} ft.   " +
               $"Carry {_character.Inventory.TotalWeightLb:F1}/{cap:F1} lb ({enc})";
    }

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

    public void Update(GameTime gt)
    {
        var ks = Keyboard.GetState();
        bool tab = ks.IsKeyDown(Keys.Tab);
        bool esc = ks.IsKeyDown(Keys.Escape);
        bool tabPressed = tab && !_tabWasDown;
        bool escPressed = esc && !_escWasDown;
        _tabWasDown = tab;
        _escWasDown = esc;
        if (tabPressed || escPressed) _game.Screens.Pop();
    }

    public void Draw(GameTime gt, SpriteBatch sb)
    {
        // Don't clear — let the play screen's last frame show through (semi-transparent overlay).
        _desktop.Render();
    }

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