using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Myra;
using Myra.Graphics2D.UI;
using Theriapolis.Core.Persistence;
using Theriapolis.Core.Rules.Character;
using Theriapolis.Core.World.Generation;

namespace Theriapolis.Game.Screens;

/// <summary>
/// Runs the world-generation pipeline on a background thread and shows per-stage progress.
/// Transitions to WorldMapScreen when generation is complete.
/// </summary>
public sealed class WorldGenProgressScreen : IScreen
{
    private readonly ulong _seed;
    private readonly SaveBody? _restoreFromSave;
    private readonly SaveHeader? _savedHeader;
    private readonly Character? _pendingCharacter;
    private readonly string? _pendingName;
    private Game1 _game = null!;
    private Desktop _desktop = null!;
    private Label? _stageLabel;
    private Label?  _progressLabel;

    private WorldGenContext? _ctx;
    private Task? _genTask;
    private volatile float _progress;
    private volatile string _stageName = "Initialising...";
    private volatile bool _complete;
    private volatile string? _error;

    public WorldGenProgressScreen(
        ulong seed,
        SaveBody? restoreFromSave = null,
        SaveHeader? savedHeader = null,
        Character? pendingCharacter = null,
        string? pendingName = null)
    {
        _seed             = seed;
        _restoreFromSave  = restoreFromSave;
        _savedHeader      = savedHeader;
        _pendingCharacter = pendingCharacter;
        _pendingName      = pendingName;
    }

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

    private void BuildUI()
    {
        var root = new VerticalStackPanel
        {
            Spacing = 16,
            HorizontalAlignment = HorizontalAlignment.Center,
            VerticalAlignment   = VerticalAlignment.Center,
        };

        root.Widgets.Add(new Label
        {
            Text = $"Generating world... (seed: 0x{_seed:X})",
            HorizontalAlignment = HorizontalAlignment.Center,
        });

        _progressLabel = new Label
        {
            Text  = "[          ]  0%",
            HorizontalAlignment = HorizontalAlignment.Center,
        };
        root.Widgets.Add(_progressLabel);

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

        _desktop = new Desktop { Root = root };
    }

    private void StartGeneration()
    {
        string dataDir = _game.ContentDataDirectory;
        _genTask = Task.Run(() =>
        {
            try
            {
                _ctx = new WorldGenContext(_seed, dataDir)
                {
                    ProgressCallback = (name, frac) =>
                    {
                        _stageName = name;
                        _progress  = frac;
                    },
                    Log = msg => System.Diagnostics.Debug.WriteLine(msg),
                };
                WorldGenerator.RunAll(_ctx);
                _complete = true;
            }
            catch (Exception ex)
            {
                // Unwrap AggregateException to get the real inner message
                var inner = ex is AggregateException ae ? ae.Flatten().InnerException ?? ex : ex;
                _error = inner.ToString(); // full type + message + stack trace
            }
        });
    }

    public void Update(GameTime gameTime)
    {
        if (_error is not null)
        {
            // Show error on screen so it is visible; do NOT pop automatically.
            System.Diagnostics.Debug.WriteLine($"[WorldGen ERROR] {_error}");
            if (_stageLabel    is not null) _stageLabel.Text    = "ERROR — press Escape to go back";
            if (_progressLabel is not null) _progressLabel.Text = _error.Length > 80
                ? _error[..80] + "..."
                : _error;
            // Write full error to a log file next to the exe for post-mortem diagnosis
            try
            {
                string logPath = Path.Combine(
                    AppContext.BaseDirectory, "worldgen_error.log");
                File.WriteAllText(logPath,
                    $"[{DateTime.Now:u}] WorldGen ERROR\n{_error}\n");
            }
            catch { /* best-effort */ }

            // Only pop when the user presses Escape
            if (Microsoft.Xna.Framework.Input.Keyboard.GetState()
                    .IsKeyDown(Microsoft.Xna.Framework.Input.Keys.Escape))
                _game.Screens.Pop();
            return;
        }

        if (_complete && _ctx is not null)
        {
            // Stage-hash check: a soft warning is fine for Phase 4. We log
            // mismatches but proceed — saves anchored only by player position
            // and chunk deltas tolerate small worldgen drift.
            if (_savedHeader is not null) CompareStageHashes();

            if (_restoreFromSave is not null)
                _game.Screens.Push(new PlayScreen(_ctx, _restoreFromSave));
            else if (_pendingCharacter is not null)
                _game.Screens.Push(new PlayScreen(_ctx, _pendingCharacter, _pendingName ?? "Wanderer"));
            else
                _game.Screens.Push(new PlayScreen(_ctx));

            _complete = false;
            return;
        }

        // Update UI progress on game thread
        int pct   = (int)(_progress * 100f);
        int filled = pct / 10;
        string bar = new string('#', filled) + new string(' ', 10 - filled);
        if (_progressLabel is not null) _progressLabel.Text = $"[{bar}] {pct,3}%";
        if (_stageLabel    is not null) _stageLabel.Text    = _stageName;
    }

    public void Draw(GameTime gameTime, SpriteBatch spriteBatch)
    {
        _game.GraphicsDevice.Clear(new Color(10, 10, 20));
        _desktop.Render();
    }

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

    private void CompareStageHashes()
    {
        if (_savedHeader is null || _ctx is null) return;
        int mismatches = 0;
        foreach (var kv in _ctx.World.StageHashes)
        {
            if (!_savedHeader.StageHashes.TryGetValue(kv.Key, out var sv)) continue;
            string current = $"0x{kv.Value:X}";
            if (!string.Equals(sv, current, StringComparison.OrdinalIgnoreCase))
            {
                mismatches++;
                System.Diagnostics.Debug.WriteLine(
                    $"[Save migration] Stage '{kv.Key}' hash drift: saved={sv}, current={current}");
            }
        }
        if (mismatches > 0)
            System.Diagnostics.Debug.WriteLine(
                $"[Save migration] {mismatches} stage(s) drifted; loading anyway (soft).");
    }
}
