using System.Globalization;
using System.IO;
using NexusShell.Models;

namespace NexusShell.Services;

public sealed class NexusPhotosLibraryService
{
    private static readonly CultureInfo DaDk = CultureInfo.GetCultureInfo("da-DK");

    public static readonly IReadOnlySet<string> SupportedExtensions =
        new HashSet<string>(StringComparer.OrdinalIgnoreCase)
        {
            ".jpg", ".jpeg", ".png", ".webp", ".bmp",
        };

    private readonly string _picturesPath;
    private readonly string _downloadsPath;
    private readonly string _screenshotsPath;
    private readonly string _cameraPath;
    private IReadOnlyList<NexusPhotoItem> _cachedPhotos = [];
    private DateTime _lastScanUtc = DateTime.MinValue;

    public NexusPhotosLibraryService()
    {
        _picturesPath = Environment.GetFolderPath(Environment.SpecialFolder.MyPictures);
        var profile = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
        _downloadsPath = Path.Combine(profile, "Downloads");
        _screenshotsPath = Path.Combine(_picturesPath, "Screenshots");
        _cameraPath = Path.Combine(_picturesPath, "Camera Roll");
    }

    public string PicturesPath => _picturesPath;
    public string DownloadsPath => _downloadsPath;
    public string ScreenshotsPath => _screenshotsPath;
    public string CameraPath => _cameraPath;

    public bool IsSupportedExtension(string path) =>
        SupportedExtensions.Contains(Path.GetExtension(path));

    public IReadOnlyList<(NexusPhotosSection Section, string Path, string Label)> GetScanRoots() =>
    [
        (NexusPhotosSection.Pictures, _picturesPath, "Billeder"),
        (NexusPhotosSection.Downloads, _downloadsPath, "Downloads"),
        (NexusPhotosSection.Screenshots, _screenshotsPath, "Skærmbilleder"),
        (NexusPhotosSection.Camera, _cameraPath, "Kamera"),
    ];

    public IReadOnlyList<NexusPhotoItem> ScanLibrary(bool forceRefresh = false)
    {
        if (!forceRefresh && _cachedPhotos.Count > 0 &&
            DateTime.UtcNow - _lastScanUtc < TimeSpan.FromMinutes(2))
        {
            return _cachedPhotos;
        }

        var photos = new List<NexusPhotoItem>();
        var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

        foreach (var (_, rootPath, label) in GetScanRoots())
        {
            if (!Directory.Exists(rootPath))
            {
                continue;
            }

            ScanDirectory(rootPath, label, photos, seen, recursive: true);
        }

        _cachedPhotos = photos
            .OrderByDescending(static p => p.ModifiedUtc)
            .ToList();
        _lastScanUtc = DateTime.UtcNow;
        return _cachedPhotos;
    }

    public IReadOnlyList<NexusPhotoItem> GetPhotosForSection(NexusPhotosSection section, IReadOnlyList<NexusPhotoItem>? source = null)
    {
        var all = source ?? ScanLibrary();
        return section switch
        {
            NexusPhotosSection.Favorites => all.Where(static p => p.IsFavorite).ToList(),
            NexusPhotosSection.Recent => all.OrderByDescending(static p => p.ModifiedUtc).Take(200).ToList(),
            NexusPhotosSection.Pictures => FilterByRoot(all, _picturesPath),
            NexusPhotosSection.Screenshots => FilterByRoot(all, _screenshotsPath),
            NexusPhotosSection.Downloads => FilterByRoot(all, _downloadsPath),
            NexusPhotosSection.Camera => FilterByRoot(all, _cameraPath),
            _ => all,
        };
    }

    public IReadOnlyList<NexusPhotoItem> GetPhotosInFolder(string folderPath, IReadOnlyList<NexusPhotoItem>? source = null)
    {
        var normalized = Path.GetFullPath(folderPath).TrimEnd('\\');
        return (source ?? ScanLibrary())
            .Where(p => Path.GetFullPath(p.FolderPath).TrimEnd('\\')
                .StartsWith(normalized, StringComparison.OrdinalIgnoreCase))
            .ToList();
    }

    public IReadOnlyList<NexusPhotoFolderSummary> GetFolderSummaries(IReadOnlyList<NexusPhotoItem>? source = null)
    {
        return (source ?? ScanLibrary())
            .GroupBy(static p => p.FolderPath, StringComparer.OrdinalIgnoreCase)
            .Select(g =>
            {
                var latest = g.OrderByDescending(static p => p.ModifiedUtc).First();
                return new NexusPhotoFolderSummary
                {
                    FolderPath = g.Key,
                    Label = latest.FolderLabel,
                    PhotoCount = g.Count(),
                    TotalBytes = g.Sum(static p => p.FileSizeBytes),
                    LatestModifiedUtc = g.Max(static p => p.ModifiedUtc),
                    CoverPhotoPath = latest.FullPath,
                };
            })
            .OrderByDescending(static f => f.PhotoCount)
            .ToList();
    }

    public IReadOnlyList<NexusPhotoFolderSummary> GetLargestFolders(int count, IReadOnlyList<NexusPhotoItem>? source = null) =>
        GetFolderSummaries(source).Take(count).ToList();

    public IReadOnlyList<NexusPhotoItem> GetRecentPhotos(int count, IReadOnlyList<NexusPhotoItem>? source = null) =>
        (source ?? ScanLibrary()).OrderByDescending(static p => p.ModifiedUtc).Take(count).ToList();

    public IReadOnlyList<NexusPhotoItem> GetRecentlyImported(int count, IReadOnlyList<NexusPhotoItem>? source = null) =>
        (source ?? ScanLibrary()).OrderByDescending(static p => p.CreatedUtc).Take(count).ToList();

    public IReadOnlyList<NexusPhotoItem> SearchLocal(string query, IReadOnlyList<NexusPhotoItem> source)
    {
        if (string.IsNullOrWhiteSpace(query))
        {
            return source;
        }

        var term = query.Trim();
        return source
            .Where(p => p.FileName.Contains(term, StringComparison.OrdinalIgnoreCase) ||
                        p.FolderLabel.Contains(term, StringComparison.OrdinalIgnoreCase))
            .OrderBy(static p => p.FileName, StringComparer.OrdinalIgnoreCase)
            .ToList();
    }

    public string GetUnsupportedFormatMessage(string path)
    {
        var ext = Path.GetExtension(path);
        var supported = string.Join(", ", SupportedExtensions.Select(static e => e.TrimStart('.')).OrderBy(static e => e));
        return $"Formatet \"{ext}\" understøttes ikke i Nexus Photos.\n\nUnderstøttede formater: {supported.ToUpperInvariant()}";
    }

    private static IReadOnlyList<NexusPhotoItem> FilterByRoot(IReadOnlyList<NexusPhotoItem> all, string rootPath)
    {
        if (!Directory.Exists(rootPath))
        {
            return [];
        }

        var normalized = Path.GetFullPath(rootPath).TrimEnd('\\');
        return all
            .Where(p => Path.GetFullPath(p.FullPath).StartsWith(normalized, StringComparison.OrdinalIgnoreCase))
            .ToList();
    }

    private void ScanDirectory(
        string directoryPath,
        string rootLabel,
        List<NexusPhotoItem> photos,
        HashSet<string> seen,
        bool recursive)
    {
        IEnumerable<string> files;
        try
        {
            files = Directory.EnumerateFiles(directoryPath);
        }
        catch
        {
            return;
        }

        foreach (var filePath in files)
        {
            if (!IsSupportedExtension(filePath) || !seen.Add(filePath))
            {
                continue;
            }

            var item = TryBuildPhotoItem(filePath, rootLabel);
            if (item is not null)
            {
                photos.Add(item);
            }
        }

        if (!recursive)
        {
            return;
        }

        IEnumerable<string> subdirs;
        try
        {
            subdirs = Directory.EnumerateDirectories(directoryPath);
        }
        catch
        {
            return;
        }

        foreach (var subdir in subdirs)
        {
            ScanDirectory(subdir, rootLabel, photos, seen, recursive: true);
        }
    }

    private NexusPhotoItem? TryBuildPhotoItem(string filePath, string rootLabel)
    {
        try
        {
            var info = new FileInfo(filePath);
            if (!info.Exists)
            {
                return null;
            }

            var (width, height) = NexusMetadataService.ReadDimensions(filePath);
            var folderPath = info.DirectoryName ?? string.Empty;
            var folderLabel = BuildFolderLabel(folderPath, rootLabel);

            return new NexusPhotoItem
            {
                FullPath = info.FullName,
                FileName = info.Name,
                FolderPath = folderPath,
                FolderLabel = folderLabel,
                FileSizeBytes = info.Length,
                CreatedUtc = info.CreationTimeUtc,
                ModifiedUtc = info.LastWriteTimeUtc,
                Width = width,
                Height = height,
                Format = info.Extension.TrimStart('.').ToUpperInvariant(),
                Extension = info.Extension,
                IsFavorite = false,
            };
        }
        catch
        {
            return null;
        }
    }

    private static string BuildFolderLabel(string folderPath, string rootLabel)
    {
        if (string.IsNullOrWhiteSpace(folderPath))
        {
            return rootLabel;
        }

        return $"{rootLabel} › {Path.GetFileName(folderPath.TrimEnd('\\'))}";
    }

    public static string FormatBytes(long bytes) => bytes switch
    {
        < 1024 => $"{bytes} B",
        < 1024 * 1024 => $"{bytes / 1024.0:0.#} KB",
        < 1024L * 1024 * 1024 => $"{bytes / (1024.0 * 1024):0.#} MB",
        _ => $"{bytes / (1024.0 * 1024 * 1024):0.#} GB",
    };

    public static string FormatDate(DateTime utc) =>
        utc.ToLocalTime().ToString("dd. MMM yyyy HH:mm", DaDk);
}
