using System.IO;
using Godot;
using Theriapolis.Core;
using Theriapolis.Core.Tactical;
using Theriapolis.Core.Util;
using Theriapolis.Core.World.Generation;
using Theriapolis.GodotHost.Platform;

namespace Theriapolis.GodotHost.Rendering;

/// <summary>
/// Standalone demo entry point for the M2+M4 unified seamless-zoom view.
/// Runs worldgen inline, spawns a placeholder player, and lets you walk
/// around with WASD. Used by the <c>--world-map</c> and <c>--tactical</c>
/// CLI flags for headless / debug viewing without going through the
/// title → wizard → progress flow.
///
/// The actual rendering — biome / polyline / settlement / chunk layers
/// and the camera — lives in <see cref="WorldRenderNode"/>, which is
/// shared with M7's <see cref="Scenes.PlayScreen"/>. This shell just
/// owns the demo's local player position and streaming loop.
/// </summary>
public partial class WorldView : Node2D
{
    private readonly ulong _seed;
    private readonly int _startWorldTileX;
    private readonly int _startWorldTileY;
    private readonly float _initialZoom;

    // World-pixel movement speed. 32 wp = 1 world tile, so 96 = ~3 tiles/sec.
    private const float MoveSpeedWorldPx = 96f;
    private const int   StreamingBufferWorldTiles = 2;
    private const float StreamRadiusZoomMin = WorldRenderNode.TacticalRenderZoomMin;

    private ChunkStreamer? _streamer;
    private WorldRenderNode? _render;
    private Vec2 _playerPos;
    private PlayerMarker? _playerMarker;

    public WorldView(ulong seed, int startWorldTileX = 128, int startWorldTileY = 128, float initialZoom = 0f)
    {
        _seed = seed;
        _startWorldTileX = startWorldTileX;
        _startWorldTileY = startWorldTileY;
        _initialZoom = initialZoom;
    }

    public override void _Ready()
    {
        string dataDir = ContentPaths.DataDir;
        if (!Directory.Exists(dataDir))
        {
            GD.PrintErr($"[world] Data directory not found: {dataDir}");
            return;
        }

        GD.Print($"[world] seed=0x{_seed:X} start-tile=({_startWorldTileX},{_startWorldTileY})");
        var ctx = new WorldGenContext(_seed, dataDir);
        WorldGenerator.RunAll(ctx);
        var world = ctx.World;
        GD.Print($"[world] worldgen done — rivers={world.Rivers.Count} " +
                 $"roads={world.Roads.Count} rails={world.Rails.Count} " +
                 $"settlements={world.Settlements.Count} bridges={world.Bridges.Count}");

        _render = new WorldRenderNode();
        AddChild(_render);
        _render.Initialize(world, _initialZoom);

        _streamer = new ChunkStreamer(_seed, world, new InMemoryChunkDeltaStore());
        _streamer.OnChunkLoaded   += _render.AddChunkNode;
        _streamer.OnChunkEvicting += _render.RemoveChunkNode;

        _playerPos = new Vec2(
            _startWorldTileX * C.WORLD_TILE_PIXELS + C.WORLD_TILE_PIXELS * 0.5f,
            _startWorldTileY * C.WORLD_TILE_PIXELS + C.WORLD_TILE_PIXELS * 0.5f);

        _playerMarker = new PlayerMarker { Position = new Vector2(_playerPos.X, _playerPos.Y) };
        AddChild(_playerMarker);

        _render.Camera.Position = new Vector2(_playerPos.X, _playerPos.Y);
        StreamIfTactical();
    }

    public override void _Process(double delta)
    {
        if (_render is null || _playerMarker is null) return;

        Vector2 dir = Vector2.Zero;
        if (Godot.Input.IsKeyPressed(Key.W) || Godot.Input.IsKeyPressed(Key.Up))    dir.Y -= 1;
        if (Godot.Input.IsKeyPressed(Key.S) || Godot.Input.IsKeyPressed(Key.Down))  dir.Y += 1;
        if (Godot.Input.IsKeyPressed(Key.A) || Godot.Input.IsKeyPressed(Key.Left))  dir.X -= 1;
        if (Godot.Input.IsKeyPressed(Key.D) || Godot.Input.IsKeyPressed(Key.Right)) dir.X += 1;

        if (dir != Vector2.Zero)
        {
            dir = dir.Normalized();
            float step = MoveSpeedWorldPx * (float)delta;
            _playerPos = new Vec2(_playerPos.X + dir.X * step, _playerPos.Y + dir.Y * step);

            float maxX = C.WORLD_WIDTH_TILES * C.WORLD_TILE_PIXELS - 1f;
            float maxY = C.WORLD_HEIGHT_TILES * C.WORLD_TILE_PIXELS - 1f;
            _playerPos = new Vec2(
                Mathf.Clamp(_playerPos.X, 0f, maxX),
                Mathf.Clamp(_playerPos.Y, 0f, maxY));

            StreamIfTactical();
        }

        var pos = new Vector2(_playerPos.X, _playerPos.Y);
        _playerMarker.Position = pos;
        _render.Camera.Position = pos;

        // Counter-scale the marker so its on-screen size stays constant.
        float zoom = _render.Camera.Zoom.X;
        if (zoom > 0f)
            _playerMarker.Scale = new Vector2(1f / zoom, 1f / zoom);
    }

    private void StreamIfTactical()
    {
        if (_streamer is null || _render is null) return;
        if (_render.Camera.Zoom.X < StreamRadiusZoomMin) return;

        Vector2 viewport = GetViewport().GetVisibleRect().Size;
        float halfExtentWorldPx = Mathf.Max(viewport.X, viewport.Y) / _render.Camera.Zoom.X * 0.5f;
        int halfExtentTiles = Mathf.CeilToInt(halfExtentWorldPx / C.WORLD_TILE_PIXELS);
        int radius = halfExtentTiles + StreamingBufferWorldTiles;

        _streamer.EnsureLoadedAround(_playerPos, radius);
    }
}
