using Harmonia.Core.Tags;

namespace Harmonia.Core.Imaging;

public class AudioImageExtractor : IAudioImageExtractor
{
    private readonly ITagResolver _tagResolver;
    private readonly string[] _searchPatterns;
    private readonly string[] _imageExtensions;

    public AudioImageExtractor(ITagResolver tagResolver)
    {
        _tagResolver = tagResolver;
        _searchPatterns = GetSearchPatterns();
        _imageExtensions = GetImageExtensions();
    }

    protected virtual string[] GetSearchPatterns()
    {
        return
        [
            @"*%filename%*",
            @"*folder*",
            @"*front*",
            @"*cover*",
            @"Cover\*folder*",
            @"Cover\*front*",
            @"Cover\*cover*",
            @"Cover\*",
            @"Scans\*folder*",
            @"Scans\*front*",
            @"Scans\*cover*",
            @"Scans\*",
            @"*"
        ];
    }
    

    protected virtual string[] GetImageExtensions()
    {
        return
        [
            "jpg",
            "jpeg",
            "png"
        ];
    }

    public async Task<SongPictureInfo?> ExtractImageAsync(string path, CancellationToken cancellationToken)
    {
        SongTagInfo songTagInfo = _tagResolver.GetSongTagInfo(path);

        return await ExtractImageAsync(path, songTagInfo, cancellationToken);
    }

    public async Task<SongPictureInfo?> ExtractImageAsync(string path, SongTagInfo songTagInfo, CancellationToken cancellationToken)
    {
        if (songTagInfo.FrontCover != null)
        {
            using MemoryStream memoryStream = new(songTagInfo.FrontCover.Data);

            return await SongPictureInfo.FromStreamAsync(memoryStream, cancellationToken);
        }
        else
        {
            string? imagePath = GetImagePath(path);

            if (string.IsNullOrWhiteSpace(imagePath))
                return null;

            return await SongPictureInfo.FromFileAsync(imagePath, cancellationToken);
        }
    }

    private string? GetImagePath(string path)
    {
        string? directoryName = Path.GetDirectoryName(path);

        if (string.IsNullOrWhiteSpace(directoryName))
            return null;

        string[] fileNames;
        string extension;

        foreach (string searchPattern in _searchPatterns)
        {
            fileNames = GetFilesFromDirectory(directoryName, searchPattern);

            foreach (string fileName in fileNames)
            {
                extension = Path.GetExtension(fileName)?.Replace(".", "") ?? string.Empty;

                if (_imageExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase) == false)
                    continue;

                return fileName;
            }
        }

        return null;
    }

    private static string[] GetFilesFromDirectory(string path, string searchPattern)
    {
        try
        {
            return Directory.GetFiles(path, searchPattern);
        }
        catch (DirectoryNotFoundException)
        {
            return [];
        }
    }
}