using Godot;
using System.Collections.Generic;

namespace Mr.BrickAdventures.Autoloads;

public partial class SettingsManager : Node
{
    public static SettingsManager Instance { get; private set; }

    public float GamepadDeadzone { get; set; } = 0.2f;
    public float GamepadSensitivity { get; set; } = 1.0f;
    public string WindowMode { get; set; } = "fullscreen";
    public Vector2I Resolution { get; set; } = new Vector2I(1920, 1080);

    private static readonly List<Vector2I> CuratedResolutions = new()
    {
        // 4:3
        new Vector2I(640, 480),
        new Vector2I(800, 600),
        new Vector2I(1024, 768),
        new Vector2I(1280, 960),
        new Vector2I(1600, 1200),
        // 16:9
        new Vector2I(1280, 720),
        new Vector2I(1366, 768),
        new Vector2I(1600, 900),
        new Vector2I(1920, 1080),
        new Vector2I(2560, 1440),
        new Vector2I(3840, 2160),
        // 16:10
        new Vector2I(1280, 800),
        new Vector2I(1440, 900),
        new Vector2I(1920, 1200),
        new Vector2I(2560, 1600),
    };

    private readonly List<Vector2I> _customResolutions = new();

    private static readonly string[] DeadzoneActions = { "left", "right", "up", "down" };

    public override void _Ready()
    {
        Instance = this;

        if (ConfigFileHandler.Instance == null)
        {
            GD.PushError("SettingsManager: ConfigFileHandler.Instance is null");
            return;
        }

        var cfg = ConfigFileHandler.Instance.SettingsConfig;

        // --- display_settings ---
        WindowMode = cfg.GetValue("display_settings", "window_mode", Variant.From("fullscreen")).AsString();
        var resStr = cfg.GetValue("display_settings", "resolution", Variant.From("1920x1080")).AsString();
        Resolution = ParseResolution(resStr, new Vector2I(1920, 1080));

        var customResStr = cfg.GetValue("display_settings", "custom_resolutions", Variant.From("")).AsString();
        if (!string.IsNullOrWhiteSpace(customResStr))
        {
            foreach (var part in customResStr.Split(','))
            {
                var r = ParseResolution(part.Trim(), Vector2I.Zero);
                if (r != Vector2I.Zero)
                    _customResolutions.Add(r);
            }
        }

        ApplyDisplaySettings();

        // --- gameplay_settings ---
        GamepadDeadzone = (float)cfg.GetValue("gameplay_settings", "gamepad_deadzone", Variant.From(0.2)).AsDouble();
        GamepadSensitivity = (float)cfg.GetValue("gameplay_settings", "gamepad_sensitivity", Variant.From(1.0)).AsDouble();

        ApplyGamepadDeadzone();

        // --- input_settings ---
        ApplyInputBindings(cfg);
    }

    public override void _ExitTree()
    {
        if (Instance == this) Instance = null;
    }

    // ── public API ───────────────────────────────────────────────────────────

    public List<Vector2I> GetAllResolutions()
    {
        var all = new List<Vector2I>(CuratedResolutions);
        all.AddRange(_customResolutions);
        return all;
    }

    public void SaveDisplaySettings()
    {
        var cfg = ConfigFileHandler.Instance.SettingsConfig;
        cfg.SetValue("display_settings", "window_mode", WindowMode);
        cfg.SetValue("display_settings", "resolution", $"{Resolution.X}x{Resolution.Y}");
        cfg.SetValue("display_settings", "custom_resolutions", BuildCustomResolutionsString());
        cfg.Save(ConfigFileHandler.SettingsPath);
    }

    public void SaveGameplaySettings()
    {
        var cfg = ConfigFileHandler.Instance.SettingsConfig;
        cfg.SetValue("gameplay_settings", "gamepad_deadzone", (double)GamepadDeadzone);
        cfg.SetValue("gameplay_settings", "gamepad_sensitivity", (double)GamepadSensitivity);
        cfg.Save(ConfigFileHandler.SettingsPath);
    }

    public void SaveInputSettings()
    {
        var cfg = ConfigFileHandler.Instance.SettingsConfig;
        foreach (var action in InputMap.GetActions())
        {
            if (action.ToString().StartsWith("ui_")) continue;
            foreach (var ev in InputMap.ActionGetEvents(action))
            {
                if (ev is InputEventKey key)
                {
                    cfg.SetValue("input_settings", action, (long)key.PhysicalKeycode);
                    break;
                }
            }
        }
        cfg.Save(ConfigFileHandler.SettingsPath);
    }

    // ── apply helpers ────────────────────────────────────────────────────────

    public void ApplyDisplaySettings()
    {
        switch (WindowMode)
        {
            case "fullscreen":
                DisplayServer.WindowSetMode(DisplayServer.WindowMode.Fullscreen);
                break;
            case "borderless":
                DisplayServer.WindowSetMode(DisplayServer.WindowMode.Windowed);
                DisplayServer.WindowSetFlag(DisplayServer.WindowFlags.Borderless, true);
                DisplayServer.WindowSetSize(Resolution);
                break;
            default: // "windowed"
                DisplayServer.WindowSetMode(DisplayServer.WindowMode.Windowed);
                DisplayServer.WindowSetFlag(DisplayServer.WindowFlags.Borderless, false);
                DisplayServer.WindowSetSize(Resolution);
                break;
        }
    }

    public void ApplyGamepadDeadzone()
    {
        foreach (var action in DeadzoneActions)
        {
            if (InputMap.HasAction(action))
                InputMap.ActionSetDeadzone(action, GamepadDeadzone);
        }
    }

    private static void ApplyInputBindings(ConfigFile cfg)
    {
        if (!cfg.HasSection("input_settings")) return;

        foreach (var actionName in cfg.GetSectionKeys("input_settings"))
        {
            if (actionName.StartsWith("ui_")) continue;
            if (!InputMap.HasAction(actionName)) continue;

            var scancode = (Key)(long)cfg.GetValue("input_settings", actionName);

            // Remove existing keyboard events for this action
            var events = InputMap.ActionGetEvents(actionName);
            foreach (var ev in events)
            {
                if (ev is InputEventKey)
                    InputMap.ActionEraseEvent(actionName, ev);
            }

            // Add the saved key
            var newKey = new InputEventKey { PhysicalKeycode = scancode };
            InputMap.ActionAddEvent(actionName, newKey);
        }
    }

    // ── util ─────────────────────────────────────────────────────────────────

    private static Vector2I ParseResolution(string s, Vector2I fallback)
    {
        var parts = s.Split('x');
        if (parts.Length == 2 && int.TryParse(parts[0], out var w) && int.TryParse(parts[1], out var h))
            return new Vector2I(w, h);
        return fallback;
    }

    private string BuildCustomResolutionsString()
    {
        var parts = new List<string>();
        foreach (var r in _customResolutions)
            parts.Add($"{r.X}x{r.Y}");
        return string.Join(",", parts);
    }
}
