using System.IO;
using System.Text.Json;
using NexusShell.Models;

namespace NexusShell.Services;

public sealed class NexusNavigationService
{
    private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true };

    private readonly NexusFileSystemService _fileSystem;
    private readonly string _recentPath;
    private readonly List<string> _backStack = [];
    private readonly List<string> _forwardStack = [];
    private readonly List<string> _recentPaths = [];
    private string _currentPath = string.Empty;

    public NexusNavigationService(NexusFileSystemService fileSystem)
    {
        _fileSystem = fileSystem;
        _recentPath = Path.Combine(_fileSystem.NexusRoot, "files-recent.json");
        LoadRecent();
    }

    public string CurrentPath => _currentPath;
    public bool CanGoBack => _backStack.Count > 0;
    public bool CanGoForward => _forwardStack.Count > 0;

    public IReadOnlyList<string> RecentPaths => _recentPaths;

    public string ResolveInitialPath(string? hint)
    {
        if (string.IsNullOrWhiteSpace(hint))
        {
            return _fileSystem.GetDocumentsPath();
        }

        return hint.ToLowerInvariant() switch
        {
            "documents" or "dokumenter" or "shell:documentslibrary" => _fileSystem.GetDocumentsPath(),
            "downloads" => _fileSystem.ResolveLocationId(NexusPaths.DownloadsId),
            "desktop" or "skrivebord" => _fileSystem.ResolveLocationId(NexusPaths.DesktopId),
            "pictures" or "billeder" => _fileSystem.ResolveLocationId(NexusPaths.PicturesId),
            "videos" or "videoer" => _fileSystem.ResolveLocationId(NexusPaths.VideosId),
            "music" or "musik" => _fileSystem.ResolveLocationId(NexusPaths.MusicId),
            "thispc" or "drives" or "denne pc" => NexusPaths.ThisPc,
            "projects" or "projekter" => NexusPaths.Projects,
            "workspaces" or "arbejdsområder" => NexusPaths.Workspaces,
            "recent" or "seneste" => NexusPaths.Recent,
            _ when hint.StartsWith("nexus:", StringComparison.OrdinalIgnoreCase) =>
                _fileSystem.ResolveLocationId(hint),
            _ when Directory.Exists(hint) => hint,
            _ => _fileSystem.GetDocumentsPath(),
        };
    }

    public void NavigateTo(string path, bool recordHistory = true)
    {
        if (string.IsNullOrWhiteSpace(path))
        {
            return;
        }

        if (recordHistory && !string.IsNullOrWhiteSpace(_currentPath) &&
            !string.Equals(_currentPath, path, StringComparison.OrdinalIgnoreCase))
        {
            _backStack.Add(_currentPath);
            _forwardStack.Clear();
        }

        _currentPath = path;
        TrackRecent(path);
    }

    public bool TryGoBack(out string? path)
    {
        path = null;
        if (_backStack.Count == 0)
        {
            return false;
        }

        _forwardStack.Add(_currentPath);
        path = _backStack[^1];
        _backStack.RemoveAt(_backStack.Count - 1);
        _currentPath = path;
        return true;
    }

    public bool TryGoForward(out string? path)
    {
        path = null;
        if (_forwardStack.Count == 0)
        {
            return false;
        }

        _backStack.Add(_currentPath);
        path = _forwardStack[^1];
        _forwardStack.RemoveAt(_forwardStack.Count - 1);
        _currentPath = path;
        return true;
    }

    public void ResetHistory(string path)
    {
        _backStack.Clear();
        _forwardStack.Clear();
        _currentPath = path;
    }

    public IReadOnlyList<NexusPathSegment> GetBreadcrumbs(string path)
    {
        var segments = new List<NexusPathSegment>
        {
            new() { Label = "Nexus Files", Path = _fileSystem.GetDocumentsPath() },
        };

        if (string.Equals(path, NexusPaths.ThisPc, StringComparison.OrdinalIgnoreCase))
        {
            segments.Add(new NexusPathSegment { Label = "Denne PC", Path = NexusPaths.ThisPc, IsLast = true });
            return segments;
        }

        if (string.Equals(path, NexusPaths.Recent, StringComparison.OrdinalIgnoreCase))
        {
            segments.Add(new NexusPathSegment { Label = "Seneste", Path = NexusPaths.Recent, IsLast = true });
            return segments;
        }

        if (string.Equals(path, _fileSystem.ProjectsPath, StringComparison.OrdinalIgnoreCase))
        {
            segments.Add(new NexusPathSegment { Label = "Projekter", Path = path, IsLast = true });
            return segments;
        }

        if (string.Equals(path, _fileSystem.WorkspacesPath, StringComparison.OrdinalIgnoreCase))
        {
            segments.Add(new NexusPathSegment { Label = "Arbejdsområder", Path = path, IsLast = true });
            return segments;
        }

        var knownRoots = _fileSystem.GetKnownRoots();
        var normalized = Path.GetFullPath(path);
        string? matchedRoot = null;
        string? rootLabel = null;

        foreach (var root in knownRoots.Keys.OrderByDescending(static r => r.Length))
        {
            if (normalized.StartsWith(root, StringComparison.OrdinalIgnoreCase))
            {
                matchedRoot = root;
                rootLabel = knownRoots[root].Label;
                break;
            }
        }

        if (matchedRoot is not null && rootLabel is not null)
        {
            segments.Add(new NexusPathSegment { Label = rootLabel, Path = matchedRoot });

            if (string.Equals(normalized, matchedRoot, StringComparison.OrdinalIgnoreCase))
            {
                segments[^1] = segments[^1] with { IsLast = true };
                return segments;
            }

            var relative = normalized[matchedRoot.Length..].TrimStart('\\', '/');
            if (!string.IsNullOrEmpty(relative))
            {
                var parts = relative.Split('\\', '/');
                var cumulative = matchedRoot;
                for (var i = 0; i < parts.Length; i++)
                {
                    cumulative = Path.Combine(cumulative, parts[i]);
                    segments.Add(new NexusPathSegment
                    {
                        Label = parts[i],
                        Path = cumulative,
                        IsLast = i == parts.Length - 1,
                    });
                }
            }

            return segments;
        }

        if (Path.IsPathRooted(path) && path.Length >= 2 && path[1] == ':')
        {
            var driveRoot = Path.GetPathRoot(path) ?? path;
            try
            {
                var drive = DriveInfo.GetDrives().FirstOrDefault(d =>
                    string.Equals(d.Name, driveRoot, StringComparison.OrdinalIgnoreCase));
                var driveLabel = drive is null
                    ? $"Drev ({driveRoot.TrimEnd('\\', ':')})"
                    : string.IsNullOrWhiteSpace(drive.VolumeLabel)
                        ? $"Drev ({driveRoot.TrimEnd('\\', ':')})"
                        : $"{drive.VolumeLabel} ({driveRoot.TrimEnd('\\', ':')})";

                segments.Add(new NexusPathSegment { Label = "Denne PC", Path = NexusPaths.ThisPc });
                segments.Add(new NexusPathSegment { Label = driveLabel, Path = driveRoot });

                var relative = normalized[driveRoot.Length..].TrimStart('\\', '/');
                if (!string.IsNullOrEmpty(relative))
                {
                    var parts = relative.Split('\\', '/');
                    var cumulative = driveRoot.TrimEnd('\\');
                    for (var i = 0; i < parts.Length; i++)
                    {
                        cumulative = Path.Combine(cumulative, parts[i]);
                        segments.Add(new NexusPathSegment
                        {
                            Label = parts[i],
                            Path = cumulative,
                            IsLast = i == parts.Length - 1,
                        });
                    }
                }
                else
                {
                    segments[^1] = segments[^1] with { IsLast = true };
                }
            }
            catch
            {
                segments.Add(new NexusPathSegment { Label = "Mappe", Path = path, IsLast = true });
            }

            return segments;
        }

        segments.Add(new NexusPathSegment { Label = "Mappe", Path = path, IsLast = true });
        return segments;
    }

    public string ToFriendlyLabel(string path)
    {
        var crumbs = GetBreadcrumbs(path);
        return crumbs.Count == 0 ? "Nexus Files" : string.Join(" > ", crumbs.Select(static c => c.Label));
    }

    private void TrackRecent(string path)
    {
        if (_fileSystem.IsKnownVirtualRoot(path))
        {
            return;
        }

        _recentPaths.RemoveAll(p => string.Equals(p, path, StringComparison.OrdinalIgnoreCase));
        _recentPaths.Insert(0, path);
        if (_recentPaths.Count > 24)
        {
            _recentPaths.RemoveRange(24, _recentPaths.Count - 24);
        }

        SaveRecent();
    }

    private void LoadRecent()
    {
        if (!File.Exists(_recentPath))
        {
            return;
        }

        try
        {
            var json = File.ReadAllText(_recentPath);
            _recentPaths.Clear();
            _recentPaths.AddRange(JsonSerializer.Deserialize<List<string>>(json) ?? []);
        }
        catch
        {
            _recentPaths.Clear();
        }
    }

    private void SaveRecent()
    {
        Directory.CreateDirectory(_fileSystem.NexusRoot);
        var json = JsonSerializer.Serialize(_recentPaths, JsonOptions);
        File.WriteAllText(_recentPath, json);
    }
}
