using System.Collections.ObjectModel;
using View_by_Distance.Metadata.Models;
using View_by_Distance.Metadata.Models.Stateless;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Properties;
using View_by_Distance.Shared.Models.Stateless;

namespace View_by_Distance.Distance.Models.Stateless;

internal static class MappedLogicA
{

    internal record MappedFile(PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName,
                               string? PersonDisplayDirectoryName,
                               FilePath FilePath);

    private static List<MappedFile> GetDisplayDirectoryAllFiles(PeopleSettings peopleSettings, ICompareSettings compareSettings, ReadOnlyCollections readOnlyCollections)
    {
        List<MappedFile> results = [];
        FilePath filePath;
        MappedFile mappedFile;
        string personKeyFormatted;
        List<string> distinct = [];
        PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName personKeyFormattedAndKeyTicksAndDisplayDirectoryName;
        foreach (PersonContainer personContainer in readOnlyCollections.PersonContainers)
        {
            if (personContainer.Key is null)
                continue;
            for (int i = personContainer.DisplayDirectoryAllFilePaths.Count - 1; i > -1; i--)
            {
                filePath = personContainer.DisplayDirectoryAllFilePaths[i];
                if (filePath.ExtensionLowered != compareSettings.FacesFileNameExtension)
                    continue;
                if (distinct.Contains(filePath.Name))
                    continue;
                distinct.Add(filePath.Name);
                personKeyFormatted = IPersonBirthday.GetFormatted(peopleSettings.PersonBirthdayFormat, personContainer.Key.Value);
                personKeyFormattedAndKeyTicksAndDisplayDirectoryName = new(personKeyFormatted, personContainer.Key.Value, filePath.Name);
                mappedFile = new(personKeyFormattedAndKeyTicksAndDisplayDirectoryName, personContainer.DisplayDirectoryName, filePath);
                results.Add(mappedFile);
            }
        }
        return results;
    }

    private static ReadOnlyCollection<MappedFile> GetMappedFiles(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, ICompareSettings compareSettings, ReadOnlyCollections readOnlyCollections, ReadOnlyCollection<MappedLogicB.Record> records)
    {
        List<MappedFile> results = [];
        long personKey;
        string checkFile;
        FilePath filePath;
        FileHolder fileHolder;
        MappedFile mappedFile;
        List<string> distinct = [];
        PersonBirthday? personBirthday;
        PersonContainer? personContainer;
        string? personDisplayDirectoryName;
        results.AddRange(GetDisplayDirectoryAllFiles(peopleSettings, compareSettings, readOnlyCollections));
        PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName personKeyFormattedAndKeyTicksAndDisplayDirectoryName;
        ReadOnlyDictionary<long, PersonContainer> keyValuePairs = PersonContainer.Extract(readOnlyCollections.PersonContainers);
        foreach (MappedLogicB.Record record in records)
        {
            personBirthday = IPersonBirthday.GetPersonBirthday(peopleSettings.PersonBirthdayFormat, record.PersonKeyFormatted);
            if (personBirthday is null)
                continue;
            if (distinct.Contains(record.MappedFaceFilePath.Name))
                continue;
            personKey = personBirthday.Value.Ticks;
            distinct.Add(record.MappedFaceFilePath.Name);
            if (!keyValuePairs.TryGetValue(personKey, out personContainer))
                personDisplayDirectoryName = record.PersonDisplayDirectoryName;
            else
                personDisplayDirectoryName = personContainer.DisplayDirectoryName;
            personKeyFormattedAndKeyTicksAndDisplayDirectoryName = new(record.PersonKeyFormatted, personKey, personDisplayDirectoryName);
            mappedFile = new(personKeyFormattedAndKeyTicksAndDisplayDirectoryName, personDisplayDirectoryName, record.MappedFaceFilePath);
            results.Add(mappedFile);
        }
        for (int i = results.Count - 1; i > -1; i--)
        {
            filePath = results[i].FilePath;
            if (filePath.Name.EndsWith(".old"))
            {
                results.RemoveAt(i);
                continue;
            }
            if (!filePath.Name.EndsWith(".abd") && !filePath.Name.EndsWith(".brt") && !filePath.Name.EndsWith(".dup") && !filePath.Name.EndsWith(".unk"))
                continue;
            checkFile = filePath.FullName[..^4];
            if (File.Exists(checkFile))
                continue;
            File.Move(filePath.FullName, checkFile);
            fileHolder = FileHolder.Get(checkFile);
            filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
            results[i] = new(results[i].PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName, results[i].PersonDisplayDirectoryName, filePath);
        }
        for (int i = results.Count - 1; i > -1; i--)
        {
            if (File.Exists(results[i].FilePath.FullName))
                continue;
            results.RemoveAt(i);
        }
        return results.AsReadOnly();
    }

    private static void MappedParallelFor(ResultSettings resultSettings, MetadataSettings metadataSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, List<ExifDirectory> exifDirectories, ReadOnlyDictionary<int, List<FilePathAndWholePercentages>> skipCollection, MappedFile mappedFile)
    {
        int? id;
        int? wholePercentages;
        const string lnk = ".lnk";
        if (!mappedFile.FilePath.Name.EndsWith(lnk))
        {
            if (mappedFile.FilePath.Id is null)
                return;
            id = mappedFile.FilePath.Id;
            wholePercentages = IMapping.GetWholePercentages(compareSettings, mappedFile.FilePath);
        }
        else
        {
            FileHolder fileHolder = FileHolder.Get(mappedFile.FilePath.FullName[..^4]);
            FilePath filePath = FilePath.Get(resultSettings, metadataSettings, fileHolder, index: null);
            if (filePath.Id is null)
                return;
            id = filePath.Id;
            wholePercentages = IMapping.GetWholePercentages(compareSettings, filePath);
        }
        if (wholePercentages is null)
            return;
        if (distanceSettings.LinkedAlpha is null && string.IsNullOrEmpty(distanceSettings.LocationContainerDebugDirectory))
        {
            if (skipCollection.TryGetValue(id.Value, out List<FilePathAndWholePercentages>? wholePercentagesCollection))
            {
                string checkFile;
                FilePath[] filePathMatches = (from l in wholePercentagesCollection where l.WholePercentages == wholePercentages select l.FilePath).ToArray();
                foreach (FilePath filePathMatch in filePathMatches)
                {
                    if (string.IsNullOrEmpty(filePathMatch.FullName) || !File.Exists(filePathMatch.FullName))
                        continue;
                    checkFile = $"{filePathMatch}.dup";
                    if (File.Exists(checkFile))
                        continue;
                    File.Move(filePathMatch.FullName, checkFile);
                    continue;
                }
            }
        }
        if (mappedFile.FilePath.Name.EndsWith(lnk) || !File.Exists(mappedFile.FilePath.FullName))
            return;
        ExifDirectory exifDirectory = IMetadata.GetExifDirectory(mappedFile.FilePath, mappedFile.PersonKeyFormattedAndKeyTicksAndDisplayDirectoryName);
        lock (exifDirectories)
            exifDirectories.Add(exifDirectory);
    }

    internal static ReadOnlyCollection<ExifDirectory> GetMapped(ResultSettings resultSettings, MetadataSettings metadataSettings, PeopleSettings peopleSettings, DistanceSettings distanceSettings, ICompareSettings compareSettings, ICompare compare, long ticks, ReadOnlyCollections readOnlyCollections)
    {
        List<ExifDirectory> results = [];
        string eDistanceContentDirectory = IResult.GetResultsDateGroupDirectory(resultSettings, nameof(E_Distance), resultSettings.ResultContent);
        ReadOnlyCollection<MappedLogicB.Record> records = MappedLogicB.DeleteEmptyDirectoriesAndGetCollection(resultSettings, metadataSettings, peopleSettings, distanceSettings, compareSettings, compare, ticks, eDistanceContentDirectory, readOnlyCollections);
        ReadOnlyCollection<MappedFile> mappedFiles = GetMappedFiles(resultSettings, metadataSettings, peopleSettings, compareSettings, readOnlyCollections, records);
        if (mappedFiles.Count > 0)
        {
            int maxDegreeOfParallelism = compareSettings.MaxDegreeOfParallelism;
            int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
            string message = $") Building Mapped Face Files Collection - {totalSeconds} total second(s)";
            ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
            ReadOnlyDictionary<int, List<FilePathAndWholePercentages>> skipNotSkipCollection = readOnlyCollections.SkipNotSkipCollection;
            compare.ConstructProgressBar(mappedFiles.Count, message);
            _ = Parallel.For(0, mappedFiles.Count, parallelOptions, (i, state) =>
            {
                compare.Tick();
                MappedParallelFor(resultSettings, metadataSettings, distanceSettings, compareSettings, results, skipNotSkipCollection, mappedFiles[i]);
            });
        }
        return results.AsReadOnly();
    }

    internal static ReadOnlyDictionary<int, ReadOnlyDictionary<int, FilePath>> Extract(ICompareSettings compareSettings, ReadOnlyCollection<ExifDirectory> exifDirectories)
    {
        Dictionary<int, ReadOnlyDictionary<int, FilePath>> results = [];
        int? wholePercentages;
        Dictionary<int, FilePath>? keyValues;
        Dictionary<int, Dictionary<int, FilePath>> keyValuePairs = [];
        foreach (ExifDirectory exifDirectory in exifDirectories)
        {
            if (exifDirectory.FilePath.Id is null)
                continue;
            if (!keyValuePairs.TryGetValue(exifDirectory.FilePath.Id.Value, out keyValues))
            {
                keyValuePairs.Add(exifDirectory.FilePath.Id.Value, []);
                if (!keyValuePairs.TryGetValue(exifDirectory.FilePath.Id.Value, out keyValues))
                    throw new Exception();
            }
            wholePercentages = IMapping.GetWholePercentages(compareSettings, exifDirectory.FilePath);
            if (wholePercentages is null)
                continue;
            keyValues.Add(wholePercentages.Value, exifDirectory.FilePath);
        }
        foreach (KeyValuePair<int, Dictionary<int, FilePath>> keyValuePair in keyValuePairs)
            results.Add(keyValuePair.Key, keyValuePair.Value.AsReadOnly());
        return results.AsReadOnly();
    }

}