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.World.Generation;
using Theriapolis.Game.Input;
using Theriapolis.Game.Platform;
using Theriapolis.Game.Rendering;

namespace Theriapolis.Game.Screens;

/// <summary>
/// World-map screen: pan with left-drag or WASD, zoom with mouse wheel.
/// Shows the biome tile map produced by Phase 1 worldgen.
/// A top-left debug overlay shows the world seed and the tile under the cursor;
/// clicking the map copies "seed=N tile=(X,Y)" to the system clipboard.
/// </summary>
public sealed class WorldMapScreen : IScreen
{
    private readonly WorldGenContext _ctx;
    private Game1 _game = null!;

    private Camera2D _camera = null!;
    private TileAtlas _atlas = null!;
    private WorldMapRenderer _renderer = null!;
    private InputManager _input = null!;
    private SpriteBatch _sb = null!;

    // Debug overlay
    private Desktop _overlayDesktop = null!;
    private Label _debugLabel = null!;
    private int _cursorTileX;
    private int _cursorTileY;

    // Click-vs-drag detection. Tile is captured at mouse-down so that incidental
    // camera pan from hand-jitter between press and release doesn't shift the
    // reported tile — at fit-zoom, one screen pixel of drag is ~15 world pixels.
    private Vector2 _mouseDownPos;
    private int _mouseDownTileX;
    private int _mouseDownTileY;
    private bool _mouseDownTracked;
    private const float ClickSlopPixels = 4f;

    public WorldMapScreen(WorldGenContext ctx)
    {
        _ctx = ctx;
    }

    public void Initialize(Game1 game)
    {
        _game  = game;
        _input = new InputManager();
        _sb    = new SpriteBatch(game.GraphicsDevice);

        var gdw = new GraphicsDeviceWrapper(game.GraphicsDevice);
        _camera = new Camera2D(gdw);

        // Start camera centred on the world
        _camera.Position = new Vector2(
            C.WORLD_WIDTH_TILES  * C.WORLD_TILE_PIXELS * 0.5f,
            C.WORLD_HEIGHT_TILES * C.WORLD_TILE_PIXELS * 0.5f);
        // Default zoom: fit the world in the window
        float fitZoom = Math.Min(
            (float)game.GraphicsDevice.Viewport.Width  / (C.WORLD_WIDTH_TILES  * C.WORLD_TILE_PIXELS),
            (float)game.GraphicsDevice.Viewport.Height / (C.WORLD_HEIGHT_TILES * C.WORLD_TILE_PIXELS));
        _camera.AdjustZoom(fitZoom / Camera2D.MinZoom - 1f, new Vector2(
            game.GraphicsDevice.Viewport.Width  * 0.5f,
            game.GraphicsDevice.Viewport.Height * 0.5f));

        // Build tile atlas from generated biome defs
        _atlas = new TileAtlas(game.GraphicsDevice);
        _atlas.GeneratePlaceholders(_ctx.World.BiomeDefs!);

        _renderer = new WorldMapRenderer(_ctx, _atlas);

        BuildOverlay();
    }

    private void BuildOverlay()
    {
        _debugLabel = new Label
        {
            Text = "",
            HorizontalAlignment = HorizontalAlignment.Left,
            VerticalAlignment   = VerticalAlignment.Top,
            Margin              = new Thickness(8),
            Padding             = new Thickness(8, 4, 8, 4),
            Background          = new SolidBrush(new Color(0, 0, 0, 180)),
        };
        _overlayDesktop = new Desktop { Root = _debugLabel };
        UpdateOverlayText();
    }

    public void Update(GameTime gameTime)
    {
        _input.Update();

        // Ignore input when the game window isn't focused. Otherwise, clicks on
        // other windows (e.g. the Claude desktop app) would still register here
        // and overwrite the clipboard with a bogus tile coordinate.
        if (!_game.IsActive) return;

        // ESC → back to title
        if (_input.JustPressed(Keys.Escape))
        {
            _game.Screens.Pop();
            return;
        }

        float dt = (float)gameTime.ElapsedGameTime.TotalSeconds;
        float panSpeed = 400f / _camera.Zoom; // world pixels per second

        // Keyboard pan (WASD / arrow keys)
        Vector2 panDir = Vector2.Zero;
        if (_input.IsDown(Keys.W) || _input.IsDown(Keys.Up))    panDir.Y -= 1;
        if (_input.IsDown(Keys.S) || _input.IsDown(Keys.Down))  panDir.Y += 1;
        if (_input.IsDown(Keys.A) || _input.IsDown(Keys.Left))  panDir.X -= 1;
        if (_input.IsDown(Keys.D) || _input.IsDown(Keys.Right)) panDir.X += 1;
        if (panDir != Vector2.Zero)
            _camera.Pan(panDir * panSpeed * dt);

        // Track mouse-down position and tile BEFORE drag-handling consumes the
        // frame. Capturing the tile here (rather than re-reading it on release)
        // ensures the clipboard reports the tile that was actually clicked,
        // independent of any camera pan the click may have incidentally caused.
        if (_input.LeftJustDown)
        {
            _mouseDownPos     = _input.MousePosition;
            var downWorld     = _camera.ScreenToWorld(_input.MousePosition);
            _mouseDownTileX   = (int)MathF.Floor(downWorld.X / C.WORLD_TILE_PIXELS);
            _mouseDownTileY   = (int)MathF.Floor(downWorld.Y / C.WORLD_TILE_PIXELS);
            _mouseDownTracked = true;
        }

        // Mouse drag pan
        var dragDelta = _input.ConsumeDragDelta(_camera);
        if (dragDelta != Vector2.Zero)
            _camera.Pan(dragDelta);

        // Mouse wheel zoom
        int scroll = _input.ScrollDelta;
        if (scroll != 0)
        {
            float zoomDelta = scroll > 0 ? 0.12f : -0.12f;
            _camera.AdjustZoom(zoomDelta, _input.MousePosition);
        }

        // Resolve cursor → tile coordinate for overlay + click handler
        var worldPos = _camera.ScreenToWorld(_input.MousePosition);
        _cursorTileX = (int)MathF.Floor(worldPos.X / C.WORLD_TILE_PIXELS);
        _cursorTileY = (int)MathF.Floor(worldPos.Y / C.WORLD_TILE_PIXELS);

        // On release without drag, copy debug info to clipboard. Use the tile
        // captured at mouse-down so hand-jitter between press and release can't
        // shift the reported tile via incidental camera pan.
        if (_input.LeftJustUp && _mouseDownTracked)
        {
            _mouseDownTracked = false;
            if (Vector2.Distance(_input.MousePosition, _mouseDownPos) <= ClickSlopPixels)
                Clipboard.TrySetText($"seed={_ctx.World.WorldSeed} tile=({_mouseDownTileX},{_mouseDownTileY})");
        }

        UpdateOverlayText();
    }

    private void UpdateOverlayText()
    {
        _debugLabel.Text =
            $"Seed: {_ctx.World.WorldSeed}\n" +
            $"Tile: ({_cursorTileX}, {_cursorTileY})";
    }

    public void Draw(GameTime gameTime, SpriteBatch _)
    {
        _game.GraphicsDevice.Clear(new Color(5, 10, 20));
        _renderer.Draw(_sb, _camera, gameTime);
        _overlayDesktop.Render();
    }

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

    // Dispose rendering resources when screen is removed
    ~WorldMapScreen() => (_renderer as IDisposable)?.Dispose();
}
