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.Data;
using Theriapolis.Core.Entities;
using Theriapolis.Core.Items;
using Theriapolis.Core.Rules.Character;
using Theriapolis.Core.Rules.Dialogue;
using Theriapolis.Core.Rules.Reputation;

namespace Theriapolis.Game.Screens;

/// <summary>
/// Phase 6 M3 — buy/sell modal pushed from <see cref="InteractionScreen"/>
/// when a dialogue option fires the <c>open_shop</c> effect.
///
/// Pricing applies the disposition modifier from
/// <see cref="ShopPricing"/>: a friendly merchant sells for cheaper, an
/// antagonistic one marks up. Hostile / Nemesis merchants refuse service
/// outright (the screen shows a refusal line and only the close button).
///
/// Phase 6 M3 ships a hand-curated stock list per merchant role
/// (innkeeper / shopkeeper / smith / alchemist) — Phase 6 M5 swaps this
/// for trade-route-driven inventories.
/// </summary>
public sealed class ShopScreen : IScreen
{
    private static readonly string[] InnkeeperStock  = { "rations_predator", "rations_prey", "poultice_universal" };
    private static readonly string[] ShopkeeperStock = { "rope_claw_braid", "torch_scent_neutral", "scent_mask_basic", "rations_predator", "poultice_universal", "healers_kit" };
    private static readonly string[] SmithStock      = { "fang_knife", "rend_sword", "thorn_blade", "paw_axe", "hide_vest", "leather_harness", "studded_leather", "chain_shirt", "buckler", "standard_shield" };
    private static readonly string[] AlchemistStock  = { "poultice_universal", "poultice_canid", "healers_kit", "scent_mask_basic", "pheromone_vial_calm", "pheromone_vial_fear" };

    private readonly NpcActor          _npc;
    private readonly Character         _pc;
    private readonly ContentResolver   _content;
    private readonly PlayScreen        _playScreen;

    private Game1 _game = null!;
    private Desktop _desktop = null!;
    private VerticalStackPanel _root = null!;
    private Label _statusLabel = null!;
    private VerticalStackPanel _stockList = null!;
    private VerticalStackPanel _bagList   = null!;
    private bool _escWasDown   = true;
    private bool _enterWasDown = true;

    public ShopScreen(NpcActor npc, Character pc, ContentResolver content, PlayScreen playScreen)
    {
        _npc        = npc;
        _pc         = pc;
        _content    = content;
        _playScreen = playScreen;
    }

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

    private int Disposition()
        => EffectiveDisposition.For(_npc, _pc, _playScreen.Reputation, _content,
                                     _playScreen.World(), _playScreen.WorldSeed());

    private string[] StockForRole(string roleTag)
    {
        if (string.IsNullOrEmpty(roleTag)) return ShopkeeperStock;
        string suffix = roleTag;
        int dot = roleTag.LastIndexOf('.');
        if (dot >= 0) suffix = roleTag[(dot + 1)..];
        return suffix.ToLowerInvariant() switch
        {
            "innkeeper"  => InnkeeperStock,
            "smith"      => SmithStock,
            "alchemist"  => AlchemistStock,
            _             => ShopkeeperStock,
        };
    }

    private void BuildLayout()
    {
        _root = new VerticalStackPanel
        {
            Spacing             = 8,
            HorizontalAlignment = HorizontalAlignment.Center,
            VerticalAlignment   = VerticalAlignment.Center,
            Padding             = new Thickness(40, 24, 40, 24),
            Background          = new SolidBrush(new Color(15, 12, 8, 240)),
            Width               = 760,
        };

        _root.Widgets.Add(new Label
        {
            Text                = $"{_npc.DisplayName} — wares",
            HorizontalAlignment = HorizontalAlignment.Center,
            TextColor           = new Color(255, 230, 170),
        });

        int disp = Disposition();
        var label = DispositionLabels.For(disp);
        if (!ShopPricing.ServiceAvailable(disp))
        {
            _root.Widgets.Add(new Label
            {
                Text                = $"\"I'll not deal with you. Get out before I call the constabulary.\"",
                HorizontalAlignment = HorizontalAlignment.Center,
                TextColor           = new Color(220, 120, 120),
                Wrap                = true,
                Width               = 660,
            });
            _root.Widgets.Add(new Label { Text = " " });
            var closeRefused = new TextButton { Text = "Leave", Width = 240, HorizontalAlignment = HorizontalAlignment.Center };
            closeRefused.Click += (_, _) => _game.Screens.Pop();
            _root.Widgets.Add(closeRefused);
            _desktop = new Desktop { Root = _root };
            return;
        }

        _root.Widgets.Add(new Label
        {
            Text                = $"[{DispositionLabels.DisplayName(label)}] · buy ×{ShopPricing.BuyMultiplier(disp):0.00}  ·  sell ×{ShopPricing.SellMultiplier(disp):0.00}",
            HorizontalAlignment = HorizontalAlignment.Center,
            TextColor           = new Color(120, 140, 180),
        });
        _statusLabel = new Label
        {
            Text                = $"Your fangs: {_pc.CurrencyFang}",
            HorizontalAlignment = HorizontalAlignment.Center,
            TextColor           = new Color(220, 200, 140),
        };
        _root.Widgets.Add(_statusLabel);
        _root.Widgets.Add(new Label { Text = " " });

        var twoCol = new HorizontalStackPanel { Spacing = 24 };

        _stockList = new VerticalStackPanel { Spacing = 2, Width = 360 };
        _stockList.Widgets.Add(new Label { Text = "BUY", TextColor = new Color(200, 180, 130) });
        twoCol.Widgets.Add(_stockList);

        _bagList = new VerticalStackPanel { Spacing = 2, Width = 360 };
        _bagList.Widgets.Add(new Label { Text = "SELL (your bag)", TextColor = new Color(200, 180, 130) });
        twoCol.Widgets.Add(_bagList);

        _root.Widgets.Add(twoCol);
        _root.Widgets.Add(new Label { Text = " " });
        _root.Widgets.Add(new Label
        {
            Text                = "(Click an item to buy/sell · Esc / Enter to close)",
            HorizontalAlignment = HorizontalAlignment.Center,
            TextColor           = new Color(120, 110, 100),
        });

        RefreshLists();
        _desktop = new Desktop { Root = _root };
    }

    private void RefreshLists()
    {
        // Rebuild buy list.
        for (int i = _stockList.Widgets.Count - 1; i >= 1; i--) _stockList.Widgets.RemoveAt(i);
        int disp = Disposition();
        var stock = StockForRole(_npc.RoleTag);
        foreach (var id in stock)
        {
            if (!_content.Items.TryGetValue(id, out var def)) continue;
            int price = ShopPricing.BuyPriceFor((int)System.Math.Round(def.CostFang), disp);
            var btn = new TextButton
            {
                Text                = $"  {def.Name,-26} {price,4}f",
                Width               = 350,
                HorizontalAlignment = HorizontalAlignment.Left,
            };
            string capturedId = def.Id;
            btn.Click += (_, _) => TryBuy(capturedId);
            _stockList.Widgets.Add(btn);
        }

        // Rebuild sell list — group by item name + count, sell one at a time.
        for (int i = _bagList.Widgets.Count - 1; i >= 1; i--) _bagList.Widgets.RemoveAt(i);
        var grouped = _pc.Inventory.Items
            .Where(it => it.EquippedAt is null)   // can't sell equipped gear without unequipping first
            .GroupBy(it => it.Def.Id)
            .OrderBy(g => g.Key, System.StringComparer.Ordinal);
        foreach (var grp in grouped)
        {
            var def = grp.First().Def;
            int totalQty = grp.Sum(it => it.Qty);
            int price = ShopPricing.SellPriceFor((int)System.Math.Round(def.CostFang), disp);
            var btn = new TextButton
            {
                Text                = $"  {def.Name,-22} ×{totalQty,2}  @ {price,4}f",
                Width               = 350,
                HorizontalAlignment = HorizontalAlignment.Left,
            };
            string capturedId = def.Id;
            btn.Click += (_, _) => TrySell(capturedId);
            _bagList.Widgets.Add(btn);
        }
        _statusLabel.Text = $"Your fangs: {_pc.CurrencyFang}";
    }

    private void TryBuy(string itemId)
    {
        if (!_content.Items.TryGetValue(itemId, out var def)) return;
        int disp  = Disposition();
        int price = ShopPricing.BuyPriceFor((int)System.Math.Round(def.CostFang), disp);
        if (_pc.CurrencyFang < price)
        {
            _statusLabel.Text = $"Not enough fangs ({_pc.CurrencyFang} / {price}).";
            return;
        }
        _pc.CurrencyFang -= price;
        _pc.Inventory.Add(def, 1);
        _playScreen.Reputation.Submit(new RepEvent
        {
            Kind      = RepEventKind.Trade,
            FactionId = _npc.FactionId,
            RoleTag   = _npc.RoleTag,
            Magnitude = 1,                        // small positive bump per successful purchase
            Note      = $"bought {def.Id}",
            TimestampSeconds = _playScreen.ClockSeconds(),
        }, _content.Factions);
        RefreshLists();
    }

    private void TrySell(string itemId)
    {
        if (!_content.Items.TryGetValue(itemId, out var def)) return;
        int disp  = Disposition();
        int price = ShopPricing.SellPriceFor((int)System.Math.Round(def.CostFang), disp);
        // Remove ONE unit (smallest stack first to keep stacks tidy).
        var stack = _pc.Inventory.Items.Where(it => it.Def.Id == itemId && it.EquippedAt is null)
                                         .OrderBy(it => it.Qty)
                                         .FirstOrDefault();
        if (stack is null) return;
        stack.Qty--;
        if (stack.Qty <= 0) _pc.Inventory.Remove(stack);
        _pc.CurrencyFang += price;
        RefreshLists();
    }

    public void Update(GameTime gt)
    {
        var ks = Keyboard.GetState();
        bool esc = ks.IsKeyDown(Keys.Escape);
        bool ent = ks.IsKeyDown(Keys.Enter);
        bool escPressed = esc && !_escWasDown;
        bool entPressed = ent && !_enterWasDown;
        _escWasDown = esc; _enterWasDown = ent;
        if (escPressed || entPressed) _game.Screens.Pop();
    }

    public void Draw(GameTime gt, SpriteBatch sb) => _desktop.Render();
    public void Deactivate() { }
    public void Reactivate() { RefreshLists(); }
}
