using System.Diagnostics;
using System.Text.Json;
using View_by_Distance.Metadata.Models.Stateless;
using View_by_Distance.Shared.Models.Stateless;

namespace View_by_Distance.Metadata.Models;

/// <summary>
// Dictionary<string, List<KeyValuePair<string, string>>>
/// </summary>
public class B_Metadata
{

    public List<string> AngleBracketCollection { get; }

    private readonly Serilog.ILogger? _Log;
    private readonly bool _PropertiesChangedForMetadata;
    private readonly bool _ForceMetadataLastWriteTimeToCreationTime;
    private readonly JsonSerializerOptions _WriteIndentedJsonSerializerOptions;

    public static string DateTimeFormat() => "ddd MMM dd HH:mm:ss yyyy";

    public B_Metadata(bool forceMetadataLastWriteTimeToCreationTime, bool propertiesChangedForMetadata)
    {
        AngleBracketCollection = new List<string>();
        _Log = Serilog.Log.ForContext<B_Metadata>();
        _PropertiesChangedForMetadata = propertiesChangedForMetadata;
        _ForceMetadataLastWriteTimeToCreationTime = forceMetadataLastWriteTimeToCreationTime;
        _WriteIndentedJsonSerializerOptions = new JsonSerializerOptions { WriteIndented = true };
    }

    public override string ToString()
    {
        string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true });
        return result;
    }

    private Dictionary<string, List<KeyValuePair<string, string>>> GetMetadataCollection(string subFile)
    {
        Dictionary<string, List<KeyValuePair<string, string>>> results = new();
        if (_Log is null)
            throw new NullReferenceException(nameof(_Log));
        try
        {
            object? @object;
            string? tagDescription;
            int type = (int)IExif.Tags.Orientation;
            List<string> tagNames = new();
            string key = nameof(IExif.Tags.Orientation);
            IReadOnlyList<MetadataExtractor.Directory> directories = MetadataExtractor.ImageMetadataReader.ReadMetadata(subFile);
            foreach (MetadataExtractor.Directory directory in directories)
            {
                if (!results.ContainsKey(directory.Name))
                    results.Add(directory.Name, new());
                foreach (MetadataExtractor.Tag tag in directory.Tags)
                {
                    tagNames.Add(tag.Name);
                    if (string.IsNullOrEmpty(tag.Description))
                        continue;
                    results[directory.Name].Add(new KeyValuePair<string, string>(string.Concat(tag.Type, '\t', tag.Name), tag.Description));
                }
                if (tagNames.Contains(key) && !results.ContainsKey(key))
                {
                    @object = directory.GetObject(type);
                    if (@object is null)
                        continue;
                    tagDescription = @object.ToString();
                    if (string.IsNullOrEmpty(tagDescription))
                        continue;
                    results.Add(key, new() { new(string.Concat(type, '\t', key), tagDescription) });
                }
            }
        }
        catch (Exception)
        {
            _Log.Info(string.Concat(new StackFrame().GetMethod()?.Name, " <", subFile, ">"));
        }
        return results;
    }

    public (int, List<KeyValuePair<string, string>>) GetMetadataCollection(List<Tuple<string, DateTime>> subFileTuples, List<string> parseExceptions, Property.Models.Item item)
    {
        List<KeyValuePair<string, string>> results = new();
        if (item.ImageFileHolder is null)
            throw new NullReferenceException(nameof(item.ImageFileHolder));
        Dictionary<string, List<KeyValuePair<string, string>>>? dictionary;
        string json = string.Empty;
        string[] changesFrom = Array.Empty<string>();
        List<DateTime> dateTimes = (from l in subFileTuples where changesFrom.Contains(l.Item1) select l.Item2).ToList();
        FileInfo fileInfo = new(Path.Combine(AngleBracketCollection[0].Replace("<>", "{}"), string.Concat(item.ImageFileHolder.NameWithoutExtension, ".json")));
        if (!fileInfo.Exists)
        {
            if (fileInfo.Directory?.Parent is null)
                throw new Exception();
            string parentCheck = Path.Combine(fileInfo.Directory.Parent.FullName, fileInfo.Name);
            if (File.Exists(parentCheck))
            {
                File.Move(parentCheck, fileInfo.FullName);
                fileInfo.Refresh();
            }
        }
        if (_ForceMetadataLastWriteTimeToCreationTime && !fileInfo.Exists && File.Exists(Path.ChangeExtension(fileInfo.FullName, ".delete")))
        {
            File.Move(Path.ChangeExtension(fileInfo.FullName, ".delete"), fileInfo.FullName);
            fileInfo.Refresh();
        }
        if (_ForceMetadataLastWriteTimeToCreationTime && fileInfo.Exists && fileInfo.LastWriteTime != fileInfo.CreationTime)
        {
            File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
            fileInfo.Refresh();
        }
        if (_PropertiesChangedForMetadata)
            dictionary = new();
        else if (!fileInfo.Exists)
            dictionary = new();
        else if (!fileInfo.FullName.EndsWith(".json") && !fileInfo.FullName.EndsWith(".old"))
            throw new ArgumentException("must be a *.json file");
        else if (dateTimes.Any() && dateTimes.Max() > fileInfo.LastWriteTime)
            dictionary = new();
        else
        {
            json = File.ReadAllText(fileInfo.FullName);
            try
            {
                dictionary = JsonSerializer.Deserialize<Dictionary<string, List<KeyValuePair<string, string>>>>(json);
                if (dictionary is null)
                    throw new Exception();
                subFileTuples.Add(new Tuple<string, DateTime>(nameof(B_Metadata), fileInfo.LastWriteTime));
            }
            catch (Exception)
            {
                dictionary = new();
                parseExceptions.Add(nameof(B_Metadata));
            }
        }
        if (dictionary is null || !dictionary.Any())
        {
            dictionary = GetMetadataCollection(item.ImageFileHolder.FullName);
            json = JsonSerializer.Serialize(dictionary, _WriteIndentedJsonSerializerOptions);
            bool updateDateWhenMatches = dateTimes.Any() && fileInfo.Exists && dateTimes.Max() > fileInfo.LastWriteTime;
            DateTime? dateTime = !updateDateWhenMatches ? null : dateTimes.Max();
            if (Property.Models.Stateless.IPath.WriteAllText(fileInfo.FullName, json, updateDateWhenMatches, compareBeforeWrite: true, updateToWhenMatches: dateTime))
            {
                if (!_ForceMetadataLastWriteTimeToCreationTime && (!fileInfo.Exists || fileInfo.LastWriteTime == fileInfo.CreationTime))
                    subFileTuples.Add(new Tuple<string, DateTime>(nameof(B_Metadata), DateTime.Now));
                else
                {
                    File.SetLastWriteTime(fileInfo.FullName, fileInfo.CreationTime);
                    fileInfo.Refresh();
                    subFileTuples.Add(new Tuple<string, DateTime>(nameof(B_Metadata), fileInfo.CreationTime));
                }
            }
        }
        foreach (KeyValuePair<string, List<KeyValuePair<string, string>>> keyValuePair in dictionary)
        {
            foreach (KeyValuePair<string, string> keyValue in keyValuePair.Value)
                results.Add(new(string.Concat(keyValuePair.Key, '\t', keyValue.Key), keyValue.Value));
        }
        return new(dictionary.Count, results);
    }

    public static List<KeyValuePair<string, string>> GetFiltered(List<KeyValuePair<string, string>> metadataCollection)
    {
        List<KeyValuePair<string, string>> results = new();
        foreach (KeyValuePair<string, string> keyValuePair in metadataCollection)
        {
            if (keyValuePair.Key.Contains("File") || keyValuePair.Key.Contains("JPEG") || keyValuePair.Key.Contains("Orientation") || keyValuePair.Key.Contains("Thumbnail"))
                continue;
            results.Add(new(keyValuePair.Key, keyValuePair.Value));
        }
        if (!results.Any())
            results = metadataCollection;
        return results;
    }

    public static string GetUniqueImageId(List<KeyValuePair<string, string>> metadataCollection)
    {
        string result = string.Empty;
        const string exifSubIFD = "Exif SubIFD";
        const string uniqueImageID = "42016\tUnique Image ID";
        List<string> uniqueImageIDs = (from l in metadataCollection where l.Key.StartsWith(exifSubIFD) && l.Key.EndsWith(uniqueImageID) select l.Value).ToList();
        if (uniqueImageIDs.Any())
            result = uniqueImageIDs[0];
        return result;
    }

}