namespace Harmonia.Core.Data;

public abstract class FileRepository<TObject> : IRepository<TObject> where TObject : notnull
{
    private readonly Dictionary<TObject, string> _fileNameMap = [];

    protected abstract string DirectoryName { get; }
    protected abstract string Extension { get; }

    protected abstract string GetNewFileName();
    protected abstract string Serialize(TObject playlist);
    protected abstract TObject Deserialize(Stream stream);

    public FileRepository()
    {
        if (string.IsNullOrWhiteSpace(DirectoryName))
            return;

        if (Directory.Exists(DirectoryName) == false)
            Directory.CreateDirectory(DirectoryName);

        if (string.IsNullOrWhiteSpace(Extension))
            return;

        List<string> fileNames = GetAllFileNames();
        LoadFileNamesIntoMap(fileNames);
    }

    private List<string> GetAllFileNames()
    {
        var directoryInfo = new DirectoryInfo(DirectoryName);
        var directories = directoryInfo.GetDirectories().Where(x => x.Attributes.HasFlag(FileAttributes.Hidden) == false);

        var fileInfoList = directoryInfo.EnumerateFiles("*." + Extension, SearchOption.TopDirectoryOnly).Where(x => x.Attributes.HasFlag(FileAttributes.Hidden) == false);

        return [.. fileInfoList.Select(fileInfo => fileInfo.FullName)];
    }

    private void LoadFileNamesIntoMap(List<string> fileNames)
    {
        foreach (var fileName in fileNames)
        {
            using StreamReader textReader = new(fileName);

            try
            {
                TObject obj = Deserialize(textReader.BaseStream);
                _fileNameMap.Add(obj, fileName);
            }
            catch (Exception)
            {

            }
        }
    }

    public List<TObject> Get()
    {
        return [.. _fileNameMap.Keys];
    }

    public void Save(TObject obj)
    {
        string serializedObject = Serialize(obj);

        string fileName = Path.Combine(DirectoryName, GetFileName(obj));
        string? path = Path.GetDirectoryName(fileName);

        if (string.IsNullOrWhiteSpace(path))
            return;

        if (Directory.Exists(path) == false)
            Directory.CreateDirectory(path);

        using TextWriter textWriter = new StreamWriter(fileName);
        textWriter.Write(serializedObject);
    }

    private string GetFileName(TObject obj)
    {
        if (_fileNameMap.TryGetValue(obj, out string? value))
            return value;

        string fileName = GetNewFileName();
        _fileNameMap.Add(obj, fileName);

        return fileName;
    }

    public void Delete(TObject obj)
    {
        string fileName = Path.Combine(DirectoryName, GetFileName(obj));

        if (File.Exists(fileName))
            File.Delete(fileName);
    }
}