using System.Collections.Generic;
using System.Text.Json;
using Godot;
using Mr.BrickAdventures.scripts;
using Mr.BrickAdventures.scripts.State;

namespace Mr.BrickAdventures.Autoloads;

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

    [Export] private PackedScene GhostPlayerScene { get; set; }

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

    public bool IsRecording { get; private set; } = false;
    public bool IsPlaybackEnabled { get; private set; } = true;

    private List<GhostFrame> _currentRecording = [];
    private double _startTime = 0.0;
    private int _currentLevelIndex = -1;

    public void StartRecording(int levelIndex)
    {
        if (!IsPlaybackEnabled) return;

        _currentLevelIndex = levelIndex;
        _currentRecording.Clear();
        _startTime = Time.GetTicksMsec() / 1000.0;
        IsRecording = true;
        GD.Print("Ghost recording started.");
    }

    public void StopRecording(bool levelCompleted, double finalTime)
    {
        if (!IsRecording) return;
        IsRecording = false;

        if (levelCompleted)
        {
            var bestTime = LoadBestTime(_currentLevelIndex);
            if (finalTime < bestTime)
            {
                SaveGhostData(_currentLevelIndex, finalTime);
                GD.Print($"New best ghost saved for level {_currentLevelIndex}. Time: {finalTime}");
            }
        }
        _currentRecording.Clear();
    }

    public void RecordFrame(Vector2 position)
    {
        if (!IsRecording) return;

        _currentRecording.Add(new GhostFrame
        {
            Timestamp = (Time.GetTicksMsec() / 1000.0) - _startTime,
            Position = position
        });
    }

    public void SpawnGhostPlayer(int levelIndex, Node parent)
    {
        if (!IsPlaybackEnabled || GhostPlayerScene == null) return;

        var ghostData = LoadGhostData(levelIndex);
        if (ghostData.Count > 0)
        {
            var ghostPlayer = GhostPlayerScene.Instantiate<GhostPlayer>();
            parent.AddChild(ghostPlayer);
            ghostPlayer.StartPlayback(ghostData);
            GD.Print($"Ghost player spawned for level {levelIndex}.");
        }
    }

    private void SaveGhostData(int levelIndex, double time)
    {
        var path = $"user://ghost_level_{levelIndex}.json";
        var saveData = new GhostSaveData { BestTime = time };
        foreach (var frame in _currentRecording)
            saveData.Frames.Add(new GhostFrameDto { Timestamp = frame.Timestamp, X = frame.Position.X, Y = frame.Position.Y });

        try
        {
            var json = JsonSerializer.Serialize(saveData, SaveSystem.JsonOptions);
            using var file = FileAccess.Open(path, FileAccess.ModeFlags.Write);
            file.StoreString(json);
        }
        catch (System.Exception e)
        {
            GD.PrintErr($"GhostManager: Failed to save ghost data: {e.Message}");
        }
    }

    private List<GhostFrame> LoadGhostData(int levelIndex)
    {
        var path = $"user://ghost_level_{levelIndex}.json";
        if (!FileAccess.FileExists(path)) return [];

        try
        {
            using var file = FileAccess.Open(path, FileAccess.ModeFlags.Read);
            var saveData = JsonSerializer.Deserialize<GhostSaveData>(file.GetAsText(), SaveSystem.JsonOptions);
            if (saveData == null) return [];

            var frames = new List<GhostFrame>();
            foreach (var dto in saveData.Frames)
                frames.Add(new GhostFrame { Timestamp = dto.Timestamp, Position = new Vector2(dto.X, dto.Y) });
            return frames;
        }
        catch (System.Exception e)
        {
            GD.PrintErr($"GhostManager: Failed to load ghost data: {e.Message}");
            return [];
        }
    }

    private double LoadBestTime(int levelIndex)
    {
        var path = $"user://ghost_level_{levelIndex}.json";
        if (!FileAccess.FileExists(path)) return double.MaxValue;

        try
        {
            using var file = FileAccess.Open(path, FileAccess.ModeFlags.Read);
            var saveData = JsonSerializer.Deserialize<GhostSaveData>(file.GetAsText(), SaveSystem.JsonOptions);
            return saveData?.BestTime ?? double.MaxValue;
        }
        catch
        {
            return double.MaxValue;
        }
    }
}
