using System;
using System.IO;
using Godot;
using Theriapolis.Core;
using Theriapolis.Core.Persistence;
using Theriapolis.GodotHost.Platform;
using Theriapolis.GodotHost.UI;

namespace Theriapolis.GodotHost.Scenes;

/// <summary>
/// M7.3 — slot picker for *load*. Pushed by TitleScreen's "Continue"
/// when at least one compatible save exists. Lists the autosave row
/// followed by slots 1..<see cref="C.SAVE_SLOT_COUNT"/>; reads each
/// slot's header (cheap) for the label and disables incompatible /
/// unreadable rows.
///
/// On slot click: deserialise, stash the body + header + seed on
/// <see cref="GameSession"/>, swap to <see cref="WorldGenProgressScreen"/>
/// which will hand off to <see cref="PlayScreen"/> with the
/// restore-from-save path.
///
/// Save-from-pause (write) is M7.4 territory and intentionally lives
/// in a separate widget — keeps each picker single-purpose.
/// </summary>
public partial class SaveLoadScreen : Control
{
    public override void _Ready()
    {
        Theme = CodexTheme.Build();
        SetAnchorsAndOffsetsPreset(LayoutPreset.FullRect);

        var bg = new Panel { MouseFilter = MouseFilterEnum.Ignore };
        AddChild(bg);
        bg.SetAnchorsAndOffsetsPreset(LayoutPreset.FullRect);
        MoveChild(bg, 0);

        BuildUI();
    }

    private void BuildUI()
    {
        var center = new CenterContainer { MouseFilter = MouseFilterEnum.Ignore };
        AddChild(center);
        center.SetAnchorsAndOffsetsPreset(LayoutPreset.FullRect);

        var col = new VBoxContainer { CustomMinimumSize = new Vector2(520, 0) };
        col.AddThemeConstantOverride("separation", 10);
        center.AddChild(col);

        col.AddChild(new Label
        {
            Text = "LOAD GAME",
            ThemeTypeVariation = "H2",
            HorizontalAlignment = HorizontalAlignment.Center,
        });

        // Autosave row first; numbered slots after.
        AddSlotRow(col, "Autosave", SavePaths.AutosavePath());
        for (int i = 1; i <= C.SAVE_SLOT_COUNT; i++)
            AddSlotRow(col, $"Slot {i:D2}", SavePaths.SlotPath(i));

        var spacer = new Control { CustomMinimumSize = new Vector2(0, 12) };
        col.AddChild(spacer);

        var back = new Button
        {
            Text = "← Back",
            CustomMinimumSize = new Vector2(220, 40),
            SizeFlagsHorizontal = SizeFlags.ShrinkCenter,
        };
        back.Pressed += BackToTitle;
        col.AddChild(back);
    }

    private void AddSlotRow(VBoxContainer parent, string label, string path)
    {
        string text;
        bool clickable = false;
        bool exists = File.Exists(path);
        if (exists)
        {
            try
            {
                var bytes = File.ReadAllBytes(path);
                var header = SaveCodec.DeserializeHeaderOnly(bytes);
                if (SaveCodec.IsCompatible(header))
                {
                    text = SaveSlotFormat.FormatRow(label, header);
                    clickable = true;
                }
                else
                {
                    text = $"{label} — <v{header.Version}: {SaveCodec.IncompatibilityReason(header)}>";
                }
            }
            catch (Exception ex)
            {
                text = $"{label} — <unreadable: {ex.Message}>";
            }
        }
        else
        {
            text = $"{label} — <empty>";
        }

        var btn = new Button
        {
            Text = text,
            CustomMinimumSize = new Vector2(0, 40),
            SizeFlagsHorizontal = SizeFlags.ExpandFill,
            Disabled = !clickable,
            Alignment = HorizontalAlignment.Left,
        };
        if (clickable) btn.Pressed += () => LoadSlot(path);
        parent.AddChild(btn);
    }

    private void LoadSlot(string path)
    {
        try
        {
            var bytes = File.ReadAllBytes(path);
            var (header, body) = SaveCodec.Deserialize(bytes);
            if (!SaveCodec.IsCompatible(header))
            {
                GD.PushError($"[saveload] Refused incompatible save at {path}: "
                           + SaveCodec.IncompatibilityReason(header));
                return;
            }

            var session = GameSession.From(this);
            session.Seed = header.ParseSeed();
            session.PendingRestore = body;
            session.PendingHeader = header;
            session.PendingCharacter = null;  // restore path supplies it via body

            // Swap Title → WorldGenProgress (which will swap to PlayScreen
            // once the pipeline finishes and stage-hash drift is checked).
            var parent = GetParent();
            if (parent is null) return;
            foreach (Node sibling in parent.GetChildren())
                if (sibling != this) sibling.QueueFree();
            parent.AddChild(new WorldGenProgressScreen());
            QueueFree();
        }
        catch (Exception ex)
        {
            GD.PushError($"[saveload] Failed to load {path}: {ex}");
        }
    }

    private void BackToTitle()
    {
        var parent = GetParent();
        if (parent is null) return;
        foreach (Node sibling in parent.GetChildren())
            if (sibling != this) sibling.QueueFree();
        parent.AddChild(new TitleScreen());
        QueueFree();
    }

    public override void _UnhandledInput(InputEvent @event)
    {
        if (@event is InputEventKey { Pressed: true, Keycode: Key.Escape })
        {
            GetViewport().SetInputAsHandled();
            BackToTitle();
        }
    }
}
