using System.Windows.Media;

namespace NexusShell.Models;

public enum NexusMediaKind
{
    Video,
    Audio,
}

public enum NexusMediaSection
{
    Home,
    Videos,
    Music,
    Playlists,
    Favorites,
    Recent,
    Folders,
}

public enum NexusRepeatMode
{
    Off,
    All,
    One,
}

public sealed class NexusMediaItem
{
    public required string FullPath { get; init; }
    public required string FileName { get; init; }
    public required string Title { get; init; }
    public required string FolderPath { get; init; }
    public required string FolderLabel { get; init; }
    public long FileSizeBytes { get; init; }
    public DateTime CreatedUtc { get; init; }
    public DateTime ModifiedUtc { get; init; }
    public required string Format { get; init; }
    public required string Extension { get; init; }
    public NexusMediaKind Kind { get; init; }
    public string Artist { get; init; } = "Ukendt kunstner";
    public string Album { get; init; } = "Ukendt album";
    public TimeSpan Duration { get; init; }
    public int VideoWidth { get; init; }
    public int VideoHeight { get; init; }
    public string? Codec { get; init; }
    public int? BitrateKbps { get; init; }
    public string ResolutionLabel => VideoWidth > 0 && VideoHeight > 0 ? $"{VideoWidth}×{VideoHeight}" : "—";
    public string DurationLabel => NexusMediaFormatting.FormatDuration(Duration);
    public bool IsFavorite { get; set; }
}

public sealed class NexusMediaFolderSummary
{
    public required string FolderPath { get; init; }
    public required string Label { get; init; }
    public int ItemCount { get; init; }
    public long TotalBytes { get; init; }
    public DateTime LatestModifiedUtc { get; init; }
    public NexusMediaKind DominantKind { get; init; }
}

public sealed class NexusMediaMetadata
{
    public required string Title { get; init; }
    public required string FileName { get; init; }
    public required string SizeLabel { get; init; }
    public required string DurationLabel { get; init; }
    public required string Format { get; init; }
    public required string ResolutionLabel { get; init; }
    public required string BitrateLabel { get; init; }
    public required string CodecLabel { get; init; }
    public required string ArtistLabel { get; init; }
    public required string AlbumLabel { get; init; }
    public required string FolderLabel { get; init; }
    public required string FullPath { get; init; }
    public bool HasSelection { get; init; }
}

public sealed class NexusMediaNavItem
{
    public required NexusMediaSection Section { get; init; }
    public required string Label { get; init; }
    public required string IconPath { get; init; }
}

public sealed class NexusMediaGridItem : System.ComponentModel.INotifyPropertyChanged
{
    private ImageSource? _thumbnail;
    private bool _isSelected;

    public NexusMediaGridItem(NexusMediaItem item) => Item = item;

    public NexusMediaItem Item { get; }

    public ImageSource? Thumbnail
    {
        get => _thumbnail;
        set
        {
            if (_thumbnail == value)
            {
                return;
            }

            _thumbnail = value;
            OnPropertyChanged(nameof(Thumbnail));
            OnPropertyChanged(nameof(HasThumbnail));
        }
    }

    public bool HasThumbnail => _thumbnail is not null;

    public bool IsSelected
    {
        get => _isSelected;
        set
        {
            if (_isSelected == value)
            {
                return;
            }

            _isSelected = value;
            OnPropertyChanged(nameof(IsSelected));
        }
    }

    public event System.ComponentModel.PropertyChangedEventHandler? PropertyChanged;

    private void OnPropertyChanged(string name) =>
        PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(name));
}

public sealed class NexusMediaListItem : System.ComponentModel.INotifyPropertyChanged
{
    private bool _isSelected;

    public NexusMediaListItem(NexusMediaItem item) => Item = item;

    public NexusMediaItem Item { get; }

    public bool IsSelected
    {
        get => _isSelected;
        set
        {
            if (_isSelected == value)
            {
                return;
            }

            _isSelected = value;
            PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(nameof(IsSelected)));
        }
    }

    public event System.ComponentModel.PropertyChangedEventHandler? PropertyChanged;
}

public sealed class NexusMediaHomeSection
{
    public required string Title { get; init; }
    public required string IconPath { get; init; }
    public required IReadOnlyList<NexusMediaGridItem> Items { get; init; }
}

public sealed class NexusMediaQueueItem
{
    public required NexusMediaItem Media { get; init; }
    public int Index { get; init; }
}

public sealed class NexusMediaPlaylist
{
    public required string Id { get; init; }
    public required string Name { get; init; }
    public DateTime CreatedUtc { get; init; }
    public List<string> ItemPaths { get; init; } = [];
    public int ItemCount => ItemPaths.Count;
}

public sealed class NexusMediaPlaylistSummary : System.ComponentModel.INotifyPropertyChanged
{
    public required NexusMediaPlaylist Playlist { get; init; }
    public string ItemCountLabel => $"{Playlist.ItemCount} spor";

    public event System.ComponentModel.PropertyChangedEventHandler? PropertyChanged;
}

public static class NexusMediaFormatting
{
    private static readonly System.Globalization.CultureInfo DaDk =
        System.Globalization.CultureInfo.GetCultureInfo("da-DK");

    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);

    public static string FormatDuration(TimeSpan duration)
    {
        if (duration <= TimeSpan.Zero)
        {
            return "—";
        }

        return duration.TotalHours >= 1
            ? duration.ToString(@"h\:mm\:ss")
            : duration.ToString(@"m\:ss");
    }

    public static string FormatPosition(TimeSpan position, TimeSpan duration)
    {
        var pos = FormatDuration(position);
        var dur = FormatDuration(duration);
        return dur == "—" ? pos : $"{pos} / {dur}";
    }
}
