using ShellProgressBar;
using System.Collections.ObjectModel;
using System.Drawing;
using System.Text.Json;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Stateless.Methods;
using static View_by_Distance.Map.Models.Stateless.MapLogic;

namespace View_by_Distance.Map.Models.Stateless;

internal abstract class FaceFileLogic
{

    private static void MappedParallelFor(Shared.Models.Properties.IPropertyConfiguration propertyConfiguration, Configuration configuration, ReadOnlyDictionary<int, List<(string, int)>> skipCollection, List<LocationContainer> locationContainers, MappedFile mappedFile)
    {
        int? id;
        string checkFile;
        DateOnly dateOnly;
        FilePath filePath;
        string[] fileMatches;
        FileHolder fileHolder;
        int? wholePercentages;
        const string lnk = ".lnk";
        ExifDirectory? exifDirectory;
        string personDisplayDirectoryName;
        const bool fromDistanceContent = true;
        List<(string File, int WholePercentages)>? wholePercentagesCollection;
        if (!mappedFile.FilePath.Name.EndsWith(lnk))
        {
            if (mappedFile.FilePath.Id is null)
                return;
            id = mappedFile.FilePath.Id;
            wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, mappedFile.FilePath);
        }
        else
        {
            fileHolder = IFileHolder.Get(mappedFile.FilePath.FullName[..^4]);
            filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
            if (filePath.Id is null)
                return;
            id = filePath.Id;
            wholePercentages = IMapping.GetWholePercentages(configuration.FacesFileNameExtension, filePath);
        }
        if (wholePercentages is null)
            return;
        if (configuration.LinkedAlpha is null && string.IsNullOrEmpty(configuration.LocationContainerDebugDirectory) && skipCollection.TryGetValue(id.Value, out wholePercentagesCollection))
        {
            fileMatches = (from l in wholePercentagesCollection where l.WholePercentages == wholePercentages select l.File).ToArray();
            foreach (string fileMatch in fileMatches)
            {
                if (string.IsNullOrEmpty(fileMatch) || !File.Exists(fileMatch))
                    continue;
                checkFile = $"{fileMatch}.dup";
                if (File.Exists(checkFile))
                    continue;
                File.Move(fileMatch, checkFile);
                continue;
            }
        }
        dateOnly = DateOnly.FromDateTime(new DateTime(mappedFile.FilePath.CreationTicks));
        if (mappedFile.FilePath.Name.EndsWith(lnk) || !File.Exists(mappedFile.FilePath.FullName))
            exifDirectory = null;
        else
            exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(mappedFile.FilePath);
        RectangleF? rectangle = ILocation.GetPercentagesRectangle(configuration.LocationDigits, wholePercentages.Value);
        personDisplayDirectoryName = mappedFile.PersonDisplayDirectoryName is null ? configuration.MappingDefaultName : mappedFile.PersonDisplayDirectoryName;
        LocationContainer locationContainer = new(dateOnly,
                                                  exifDirectory,
                                                  mappedFile.DirectoryNumber,
                                                  personDisplayDirectoryName,
                                                  null,
                                                  null,
                                                  mappedFile.FilePath,
                                                  fromDistanceContent,
                                                  id.Value,
                                                  null,
                                                  null,
                                                  mappedFile.PersonKey,
                                                  rectangle,
                                                  wholePercentages.Value);
        lock (locationContainers)
            locationContainers.Add(locationContainer);
    }

    private static ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> GetReadOnly(Dictionary<int, Dictionary<int, LocationContainer>> keyValuePairs)
    {
        Dictionary<int, ReadOnlyDictionary<int, LocationContainer>> results = [];
        foreach (KeyValuePair<int, Dictionary<int, LocationContainer>> keyValuePair in keyValuePairs)
            results.Add(keyValuePair.Key, new(keyValuePair.Value));
        return new(results);
    }

    internal static ReadOnlyDictionary<int, ReadOnlyDictionary<int, LocationContainer>> GetMapped(int maxDegreeOfParallelism, Property.Models.Configuration propertyConfiguration, Configuration configuration, long ticks, ReadOnlyCollection<PersonContainer> personContainers, string a2PeopleSingletonDirectory, string eDistanceContentDirectory)
    {
        Dictionary<int, Dictionary<int, LocationContainer>> results = [];
        List<LocationContainer> locationContainers = [];
        List<string> personKeyFormattedCollection = [];
        Dictionary<int, LocationContainer>? keyValuePairs;
        Dictionary<int, List<(string, int)>> skipCollection = [];
        Dictionary<int, List<(string, int)>> skipNotSkipCollection = [];
        Dictionary<string, string> personKeyFormattedToNewestPersonKeyFormatted = [];
        SetSkipCollections(configuration, personContainers, a2PeopleSingletonDirectory, skipCollection, skipNotSkipCollection);
        SetPersonCollectionsAfterSetSkipCollections(configuration, personContainers, personKeyFormattedToNewestPersonKeyFormatted, personKeyFormattedCollection);
        List<Record> records = DistanceLogic.DeleteEmptyDirectoriesAndGetCollection(propertyConfiguration, configuration, ticks, eDistanceContentDirectory, personKeyFormattedToNewestPersonKeyFormatted.AsReadOnly(), personKeyFormattedCollection.AsReadOnly());
        List<MappedFile> mappedFiles = GetMappedFiles(propertyConfiguration, configuration, personContainers, records);
        if (mappedFiles.Count > 0)
        {
            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 };
            ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
            using ProgressBar progressBar = new(mappedFiles.Count, message, options);
            _ = Parallel.For(0, mappedFiles.Count, parallelOptions, (i, state) =>
            {
                progressBar.Tick();
                MappedParallelFor(propertyConfiguration, configuration, skipCollection.AsReadOnly(), locationContainers, mappedFiles[i]);
            });
        }
        foreach (LocationContainer locationContainer in locationContainers)
        {
            if (!results.TryGetValue(locationContainer.Id, out keyValuePairs))
            {
                results.Add(locationContainer.Id, []);
                if (!results.TryGetValue(locationContainer.Id, out keyValuePairs))
                    throw new Exception();
            }
            if (keyValuePairs.ContainsKey(locationContainer.WholePercentages))
                continue;
            keyValuePairs.Add(locationContainer.WholePercentages, locationContainer);
        }
        return GetReadOnly(results);
    }

    private static void MoveUnableToMatch(FilePath filePath)
    {
        string checkFile = $"{filePath.FullName}.unk";
        if (File.Exists(filePath.FullName) && !File.Exists(checkFile))
            File.Move(filePath.FullName, checkFile);
    }

    private static void AvailableParallelFor(Configuration configuration, IFaceD dFace, List<LocationContainer> locationContainers, FilePath filePath)
    {
        string? json;
        const bool fromDistanceContent = false;
        if (filePath.Id is null)
            return;
        DateOnly dateOnly = DateOnly.FromDateTime(new DateTime(filePath.CreationTicks));
        int? wholePercentages = IMapping.GetWholePercentages(dFace.FileNameExtension, filePath);
        if (wholePercentages is null)
        {
            if (configuration.DistanceMoveUnableToMatch)
                MoveUnableToMatch(filePath);
            return;
        }
        ExifDirectory exifDirectory = Metadata.Models.Stateless.Methods.IMetadata.GetExifDirectory(filePath);
        json = Metadata.Models.Stateless.Methods.IMetadata.GetOutputResolution(exifDirectory);
        if (json is null || !json.Contains(nameof(DateTime)))
        {
            if (configuration.DistanceMoveUnableToMatch)
                MoveUnableToMatch(filePath);
            return;
        }
        FaceFile? faceFile = JsonSerializer.Deserialize(json, FaceFileGenerationContext.Default.FaceFile);
        if (faceFile is null || faceFile.Location is null)
        {
            if (configuration.DistanceMoveUnableToMatch)
                MoveUnableToMatch(filePath);
            return;
        }
        RectangleF? rectangle = ILocation.GetPercentagesRectangle(configuration.LocationDigits, wholePercentages.Value);
        if (rectangle is null)
            return;
        LocationContainer locationContainer = new(dateOnly,
                                                  exifDirectory,
                                                  null,
                                                  null,
                                                  null,
                                                  faceFile,
                                                  filePath,
                                                  fromDistanceContent,
                                                  filePath.Id.Value,
                                                  null,
                                                  null,
                                                  null,
                                                  rectangle,
                                                  wholePercentages.Value);
        lock (locationContainers)
            locationContainers.Add(locationContainer);
    }

    internal static List<LocationContainer> GetAvailable(int maxDegreeOfParallelism, Configuration configuration, IFaceD dFace, long ticks, ReadOnlyCollection<FilePath> filePaths)
    {
        List<LocationContainer> results = [];
        int totalSeconds = (int)Math.Floor(new TimeSpan(DateTime.Now.Ticks - ticks).TotalSeconds);
        ParallelOptions parallelOptions = new() { MaxDegreeOfParallelism = maxDegreeOfParallelism };
        string message = $") Building Available Face Files Collection - {totalSeconds} total second(s)";
        ProgressBarOptions options = new() { ProgressCharacter = '─', ProgressBarOnBottom = true, DisableBottomPercentage = true };
        using ProgressBar progressBar = new(filePaths.Count, message, options);
        _ = Parallel.For(0, filePaths.Count, parallelOptions, (i, state) =>
        {
            progressBar.Tick();
            AvailableParallelFor(configuration, dFace, results, filePaths[i]);
        });
        return results;
    }

}