using Godot;
using Theriapolis.GodotHost.UI;

namespace Theriapolis.GodotHost.Scenes;

/// <summary>
/// Entry-point screen — vertical button stack on a parchment field with the
/// codex title and a version label. Per port-plan §M6, exists primarily to
/// validate the design system in a non-trivial composition before the player
/// reaches character creation.
///
/// Button actions:
///   New Character — swap self for the Wizard scene under the Main parent
///                   (siblings cleared so the wizard fills the viewport).
///   Continue      — disabled until <see cref="CharacterAssembler.PersistedStatePath"/>
///                   exists; full pickup lands with the M7 play loop.
///   Quit          — shut down the engine.
/// </summary>
public partial class TitleScreen : Control
{
    private const string VersionLabel = "PORT / GODOT · M7.4";
    private const string WizardScenePath = "res://Scenes/Wizard.tscn";

    public override void _Ready()
    {
        SetAnchorsAndOffsetsPreset(LayoutPreset.FullRect);
        Theme = CodexTheme.Build();

        // Backing panel so the parchment Bg fills the viewport (the Control
        // itself paints nothing). Same pattern as Wizard.cs.
        // Note: SetAnchorsAndOffsetsPreset is required (not just AnchorRight =
        // 1) because Godot's anchor setters preserve visual position by
        // adjusting offsets — manual anchor edits leave the control at 0×0.
        var bg = new Panel { MouseFilter = MouseFilterEnum.Ignore };
        AddChild(bg);
        bg.SetAnchorsAndOffsetsPreset(LayoutPreset.FullRect);
        MoveChild(bg, 0);

        // Centered title + button stack column.
        var center = new CenterContainer { MouseFilter = MouseFilterEnum.Ignore };
        AddChild(center);
        center.SetAnchorsAndOffsetsPreset(LayoutPreset.FullRect);

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

        var titleBlock = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ShrinkCenter };
        titleBlock.AddThemeConstantOverride("separation", 4);
        col.AddChild(titleBlock);
        titleBlock.AddChild(new Label
        {
            Text = "THERIAPOLIS",
            ThemeTypeVariation = "CodexTitle",
            HorizontalAlignment = HorizontalAlignment.Center,
        });
        titleBlock.AddChild(new Label
        {
            Text = "CODEX OF BECOMING",
            ThemeTypeVariation = "Eyebrow",
            HorizontalAlignment = HorizontalAlignment.Center,
        });

        var buttonStack = new VBoxContainer { SizeFlagsHorizontal = SizeFlags.ExpandFill };
        buttonStack.AddThemeConstantOverride("separation", 12);
        col.AddChild(buttonStack);

        var newBtn = MakeMenuButton("New Character", primary: true);
        newBtn.Pressed += OnNewCharacter;
        buttonStack.AddChild(newBtn);

        var continueBtn = MakeMenuButton("Continue", primary: false);
        continueBtn.Disabled = !AnyCompatibleSaveExists();
        continueBtn.Pressed += OnContinue;
        buttonStack.AddChild(continueBtn);

        var quitBtn = MakeMenuButton("Quit", primary: false);
        quitBtn.Pressed += OnQuit;
        buttonStack.AddChild(quitBtn);

        // Version chip in the bottom-right corner — small mono Eyebrow tag,
        // sits over the parchment field at a comfortable margin.
        var versionLabel = new Label
        {
            Text = VersionLabel,
            ThemeTypeVariation = "Eyebrow",
            AnchorLeft   = 1, AnchorRight  = 1,
            AnchorTop    = 1, AnchorBottom = 1,
            OffsetLeft   = -180, OffsetTop    = -28,
            OffsetRight  = -16,  OffsetBottom = -10,
            HorizontalAlignment = HorizontalAlignment.Right,
        };
        AddChild(versionLabel);
    }

    private static Button MakeMenuButton(string text, bool primary)
    {
        var btn = new Button
        {
            Text = text,
            FocusMode = FocusModeEnum.None,
            SizeFlagsHorizontal = SizeFlags.ExpandFill,
            CustomMinimumSize = new Vector2(0, 44),
        };
        if (primary) btn.ThemeTypeVariation = "PrimaryButton";
        return btn;
    }

    private void OnNewCharacter()
    {
        var packed = ResourceLoader.Load<PackedScene>(WizardScenePath);
        if (packed is null)
        {
            GD.PushError($"[title] Failed to load {WizardScenePath}");
            return;
        }
        var parent = GetParent();
        if (parent is null) return;
        // Clear siblings so the wizard fills the viewport, then swap in.
        foreach (Node sibling in parent.GetChildren())
            if (sibling != this) sibling.QueueFree();
        var wizardNode = packed.Instantiate();
        parent.AddChild(wizardNode);
        if (wizardNode is Wizard wizard)
        {
            // "← Title" back-button (visible on step 0) emits BackToTitle.
            wizard.BackToTitle += () => SwapBackToTitle(parent);
            // M7.1 — Confirm & Begin in StepReview is forwarded by the
            // wizard as CharacterConfirmed. Stash the built character on
            // GameSession and hand off to WorldGenProgressScreen.
            wizard.CharacterConfirmed += draft => SwapToWorldGen(parent, draft);
        }
        QueueFree();
    }

    private static void SwapBackToTitle(Node parent)
    {
        foreach (Node child in parent.GetChildren()) child.QueueFree();
        parent.AddChild(new TitleScreen());
    }

    /// <summary>M7.1 hand-off: snapshot the built character + chosen
    /// name onto <see cref="GameSession"/>, default the seed (a seed-entry
    /// UI lands later), and swap to <see cref="WorldGenProgressScreen"/>.</summary>
    private static void SwapToWorldGen(Node parent, UI.CharacterDraft draft)
    {
        var session = GameSession.From(parent);
        // CharacterAssembler.LastBuilt is populated by StepReview's
        // OnConfirmPressed → TryBuild call immediately before the
        // CharacterConfirmed signal fires.
        session.PendingCharacter = CharacterAssembler.LastBuilt;
        session.PendingName = string.IsNullOrWhiteSpace(draft.CharacterName)
            ? "Wanderer"
            : draft.CharacterName;
        session.Seed = 12345UL;  // default for M7; seed-entry UI is M8+.
        session.PendingRestore = null;
        session.PendingHeader = null;

        foreach (Node child in parent.GetChildren()) child.QueueFree();
        parent.AddChild(new WorldGenProgressScreen());
    }

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

    private void OnQuit() => GetTree().Quit();

    /// <summary>True iff at least one slot under <see cref="Platform.SavePaths.SavesDir"/>
    /// has a header that <see cref="Theriapolis.Core.Persistence.SaveCodec.IsCompatible"/>
    /// accepts. Cheap: <see cref="Theriapolis.Core.Persistence.SaveCodec.DeserializeHeaderOnly"/>
    /// reads only the JSON prefix, not the binary body.</summary>
    private static bool AnyCompatibleSaveExists()
    {
        try
        {
            string dir = Platform.SavePaths.SavesDir;
            if (!System.IO.Directory.Exists(dir)) return false;
            foreach (var path in System.IO.Directory.EnumerateFiles(dir, "*.trps"))
            {
                try
                {
                    var bytes = System.IO.File.ReadAllBytes(path);
                    var header = Theriapolis.Core.Persistence.SaveCodec.DeserializeHeaderOnly(bytes);
                    if (Theriapolis.Core.Persistence.SaveCodec.IsCompatible(header)) return true;
                }
                catch { /* skip broken slot */ }
            }
        }
        catch { /* defensive */ }
        return false;
    }
}
