using System.Collections.ObjectModel;

namespace View_by_Distance.Shared.Models.Stateless.Methods;

internal abstract class PersonContainer
{

    private static List<FilePath> GetFilePaths(Properties.IPropertyConfiguration propertyConfiguration, string personDisplayDirectory)
    {
        List<FilePath> results = [];
        string[] files;
        string checkFile;
        FilePath filePath;
        string directoryName;
        List<string> distinct = [];
        Models.FileHolder fileHolder;
        string fileNameWithoutExtension;
        string personDisplayDirectoryName = Path.GetFileName(personDisplayDirectory);
        files = Directory.GetFiles(personDisplayDirectory, "*", SearchOption.TopDirectoryOnly);
        foreach (string file in files)
        {
            fileHolder = IFileHolder.Get(file);
            filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
            results.Add(filePath);
        }
        string[] directories = Directory.GetDirectories(personDisplayDirectory, "*", SearchOption.TopDirectoryOnly);
        foreach (string directory in directories)
        {
            directoryName = Path.GetFileName(directory);
            files = Directory.GetFiles(directory, "*", SearchOption.TopDirectoryOnly);
            foreach (string file in files)
            {
                fileHolder = IFileHolder.Get(file);
                filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
                if (filePath.ExtensionLowered is not ".json" and not ".pged")
                {
                    results.Add(filePath);
                    continue;
                }
                fileNameWithoutExtension = Path.GetFileNameWithoutExtension(file);
                if (string.IsNullOrEmpty(fileNameWithoutExtension) || string.IsNullOrEmpty(personDisplayDirectoryName))
                    continue;
                else if (fileNameWithoutExtension.Length == 1 && fileNameWithoutExtension[0] == personDisplayDirectoryName[0])
                {
                    if (distinct.Contains(file))
                        throw new NotSupportedException($"Move / Delete <{file}>");
                    distinct.Add(file);
                }
                else if (fileNameWithoutExtension != directoryName)
                {
                    checkFile = Path.Combine(directory, $"{fileNameWithoutExtension}{filePath.ExtensionLowered}");
                    if (!File.Exists(checkFile))
                    {
                        File.Move(file, checkFile);
                        fileHolder = IFileHolder.Get(checkFile);
                        filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
                        results.Add(filePath);
                    }
                    else
                    {
                        checkFile = Path.Combine(directory, $"{fileNameWithoutExtension}.txt");
                        if (File.Exists(checkFile))
                            File.Delete(checkFile);
                        File.Move(file, checkFile);
                    }
                    continue;
                }
                results.Add(filePath);
            }
        }
        return results;
    }

    private static List<FilePath> GetFilePaths(string facesFileNameExtension, Properties.IPropertyConfiguration propertyConfiguration, string personDisplayDirectory)
    {
        List<FilePath> results;
        string checkFile;
        FilePath filePath;
        int? wholePercentages;
        string? checkDirectory;
        Models.FileHolder fileHolder;
        string[] files = Directory.GetFiles(personDisplayDirectory, "*", SearchOption.TopDirectoryOnly);
        foreach (string file in files)
        {
            if (file.EndsWith(".lnk"))
                continue;
            fileHolder = IFileHolder.Get(file);
            filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
            wholePercentages = IMapping.GetWholePercentages(facesFileNameExtension, filePath);
            if (filePath.Id is not null && wholePercentages is not null)
                continue;
            checkDirectory = Path.GetDirectoryName(file);
            if (string.IsNullOrEmpty(checkDirectory))
                continue;
            checkDirectory = Path.Combine(checkDirectory, "_ Invalid");
            if (!Directory.Exists(checkDirectory))
                _ = Directory.CreateDirectory(checkDirectory);
            checkFile = Path.Combine(checkDirectory, Path.GetFileName(file));
            if (File.Exists(checkFile))
                File.Delete(file);
            else
                File.Move(file, checkFile);
        }
        results = GetFilePaths(propertyConfiguration, personDisplayDirectory);
        return results;
    }

    private static List<Models.PersonContainer> GetPersonContainersCollections(string facesFileNameExtension, Properties.IPropertyConfiguration propertyConfiguration, PersonDirectory personDirectory, char numberSign, string personDisplayDirectory, string personDisplayDirectoryName, int? approximateYears, List<(string PersonKeyFormatted, Models.PersonBirthday PersonBirthday)> collection)
    {
        List<Models.PersonContainer> results = [];
        long personKey;
        string[] files;
        FilePath filePath;
        const int zero = 0;
        string personKeyDirectory;
        Models.FileHolder fileHolder;
        Models.PersonContainer personContainer;
        Models.PersonBirthday[] orderedPersonBirthdays;
        List<FilePath> personDisplayDirectoryAllFilePaths = GetFilePaths(facesFileNameExtension, propertyConfiguration, personDisplayDirectory);
        foreach ((string personKeyFormatted, Models.PersonBirthday personBirthday) in collection)
        {
            orderedPersonBirthdays = (from l in collection where !l.PersonKeyFormatted.Contains(numberSign) orderby l.PersonBirthday.Value.Ticks descending select l.PersonBirthday).ToArray();
            if (orderedPersonBirthdays.Length == 0)
                personKey = collection[zero].PersonBirthday.Value.Ticks;
            else
            {
                if (personKeyFormatted.Contains(numberSign))
                    continue;
                personKey = orderedPersonBirthdays[zero].Value.Ticks;
            }
            personKeyDirectory = Path.Combine(personDisplayDirectory, personKeyFormatted);
            files = Directory.GetFiles(personKeyDirectory, "*", SearchOption.AllDirectories);
            if (files.Length == 0)
                continue;
            foreach (string file in files)
            {
                if (!file.EndsWith(".rel"))
                    continue;
                fileHolder = IFileHolder.Get(file);
                filePath = FilePath.Get(propertyConfiguration, fileHolder, index: null);
                personDisplayDirectoryAllFilePaths.Add(filePath);
            }
            personContainer = new(approximateYears, orderedPersonBirthdays, new(personDisplayDirectoryAllFilePaths), personDisplayDirectoryName, personKey, personDirectory);
            results.Add(personContainer);
        }
        return results;
    }

    internal static string? VerifyAge(char numberSign, string personDisplayDirectory, string? minusOne, string personDisplayDirectoryName, int? approximateYears, List<(string PersonKeyFormatted, Models.PersonBirthday PersonBirthday)> collection)
    {
        string? result;
        if (approximateYears is null)
            throw new NotSupportedException();
        if (collection.Count == 0)
            throw new NotSupportedException();
        const int zero = 0;
        int? updateApproximateYears;
        DateTime dateTime = DateTime.Now;
        Models.PersonBirthday[] orderedPersonBirthdays = (from l in collection where !l.PersonKeyFormatted.Contains(numberSign) orderby l.PersonBirthday.Value.Ticks descending select l.PersonBirthday).ToArray();
        TimeSpan timeSpan = new(orderedPersonBirthdays[zero].Value.Ticks - dateTime.AddYears(-approximateYears.Value).Ticks);
        if (timeSpan.TotalDays < -366)
            updateApproximateYears = approximateYears.Value + 1;
        else if (timeSpan.TotalDays > 1)
            updateApproximateYears = approximateYears.Value - 1;
        else
            updateApproximateYears = null;
        if (minusOne is null || updateApproximateYears is null)
            result = null;
        else
        {
            result = Path.Combine(minusOne, $"{personDisplayDirectoryName.Split('^')[0]}^{updateApproximateYears}");
            if (Directory.Exists(result))
                result = null;
            else
                Directory.Move(personDisplayDirectory, result);
        }
        return result;
    }

    private static (List<string?>, List<Models.PersonContainer>) GetPersonContainersGroup(string personBirthdayFormat, string facesFileNameExtension, char[] personCharacters, Properties.IPropertyConfiguration propertyConfiguration, PersonDirectory personDirectory, string[] personDisplayDirectories)
    {
        List<Models.PersonContainer> results = [];
        string? minusOne;
        int? approximateYears;
        char numberSign = '#';
        List<string?> changes = [];
        string[] personKeyDirectories;
        string? personDisplayDirectoryName;
        Models.PersonContainer personContainer;
        List<Models.PersonContainer> personContainers;
        List<(string, Models.PersonBirthday)> collection;
        List<FilePath> personDisplayDirectoryAllFilePaths;
        foreach (string personDisplayDirectory in personDisplayDirectories)
        {
            personDisplayDirectoryName = Path.GetFileName(personDisplayDirectory);
            if (string.IsNullOrEmpty(personDisplayDirectoryName))
                continue;
            approximateYears = Age.GetApproximateYears(personCharacters, personDisplayDirectoryName);
            personKeyDirectories = Directory.GetDirectories(personDisplayDirectory, "*", SearchOption.TopDirectoryOnly);
            collection = PersonBirthday.GetPersonBirthdays(personBirthdayFormat, personKeyDirectories, personDisplayDirectoryName);
            if (personDisplayDirectoryName.Contains('^'))
            {
                minusOne = Path.GetDirectoryName(personDisplayDirectory);
                if (minusOne is null)
                    continue;
                changes.Add(VerifyAge(numberSign, personDisplayDirectory, minusOne, personDisplayDirectoryName, approximateYears, collection));
            }
            if (changes.Any(l => l is not null))
                continue;
            if (collection.Count > 0)
            {
                personContainers = GetPersonContainersCollections(facesFileNameExtension, propertyConfiguration, personDirectory, numberSign, personDisplayDirectory, personDisplayDirectoryName, approximateYears, collection);
                results.AddRange(personContainers);
            }
            else
            {
                personDisplayDirectoryAllFilePaths = GetFilePaths(facesFileNameExtension, propertyConfiguration, personDisplayDirectory);
                personContainer = Models.PersonContainer.Get(approximateYears, new(personDisplayDirectoryAllFilePaths), personDisplayDirectoryName, personDirectory);
                results.Add(personContainer);
            }
        }
        return new(changes, results);
    }

    private static (List<string?>, List<Models.PersonContainer>) GetPersonContainersInnerGroups(string personBirthdayFormat, string facesFileNameExtension, char[] personCharacters, Properties.IPropertyConfiguration propertyConfiguration, string groupDirectory, string groupDirectoryName)
    {
        List<Models.PersonContainer> results = [];
        string[] segments;
        const int zero = 0;
        List<string?> changes;
        string innerGroupDirectoryName;
        PersonDirectory personDirectory;
        List<string?> allChanges = [];
        string[] personDisplayDirectories;
        const char exclamationPoint = '!';
        char @char = groupDirectoryName[zero];
        List<Models.PersonContainer> collection;
        string[] innerGroupDirectories = Directory.GetDirectories(groupDirectory, "*", SearchOption.TopDirectoryOnly);
        if (@char == exclamationPoint)
        {
            personDirectory = new(@char, "Ignore", 'U', 'U', 'U');
            (changes, collection) = GetPersonContainersGroup(personBirthdayFormat, facesFileNameExtension, personCharacters, propertyConfiguration, personDirectory, innerGroupDirectories);
            allChanges.AddRange(changes);
            results.AddRange(collection);
        }
        else
        {
            foreach (string innerGroupDirectory in innerGroupDirectories)
            {
                innerGroupDirectoryName = Path.GetFileName(innerGroupDirectory);
                segments = innerGroupDirectoryName.Split('-');
                if (segments.Length == 0)
                    throw new NotSupportedException("Misplaced directory!");
                if (segments.Length != 3)
                    continue;
                if (segments[zero] is not "Alive" and not "Dead" and not "Unknown")
                    continue;
                if (segments[1] is not "Male" and not "Female" and not "Unknown")
                    continue;
                if (segments[2] is not "Yes" and not "No" and not "Unknown")
                    continue;
                personDirectory = new(@char, innerGroupDirectoryName, segments[zero][zero], segments[1][zero], segments[2][zero]);
                personDisplayDirectories = Directory.GetDirectories(innerGroupDirectory, "*", SearchOption.TopDirectoryOnly);
                (changes, collection) = GetPersonContainersGroup(personBirthdayFormat, facesFileNameExtension, personCharacters, propertyConfiguration, personDirectory, personDisplayDirectories);
                allChanges.AddRange(changes);
                results.AddRange(collection);
            }
        }
        return new(allChanges, results);
    }

    private static List<Models.PersonContainer> GetPersonContainersGroups(string personBirthdayFormat, string facesFileNameExtension, char[] personCharacters, Properties.IPropertyConfiguration propertyConfiguration, string[] groupDirectories)
    {
        List<Models.PersonContainer> results;
        List<string?> changes;
        string groupDirectoryName;
        List<string?> allChanges = [];
        List<Models.PersonContainer> collection;
        List<Models.PersonContainer> personContainers = [];
        for (int i = 0; i < personCharacters.Length; i++)
        {
            foreach (string groupDirectory in groupDirectories)
            {
                groupDirectoryName = Path.GetFileName(groupDirectory);
                if (personCharacters[i] != groupDirectoryName.First())
                    continue;
                (changes, collection) = GetPersonContainersInnerGroups(personBirthdayFormat, facesFileNameExtension, personCharacters, propertyConfiguration, groupDirectory, groupDirectoryName);
                allChanges.AddRange(changes);
                personContainers.AddRange(collection);
            }
        }
        if (allChanges.Any(l => l is not null))
            throw new NotImplementedException($"A directory was changed restart to look for more! {string.Join(Environment.NewLine, (from l in allChanges where l is not null select l).ToArray())}");
        results = (from l in personContainers orderby l.Key is not null, l.Key select l).ToList();
        return results;
    }

    internal static List<Models.PersonContainer> GetPersonContainers(string a2PeopleSingletonDirectory, string personBirthdayFormat, char[] personCharacters, Properties.IPropertyConfiguration propertyConfiguration, string facesFileNameExtension)
    {
        List<Models.PersonContainer> results;
        if (!Directory.Exists(a2PeopleSingletonDirectory))
            _ = Directory.CreateDirectory(a2PeopleSingletonDirectory);
        string a2PeopleSingletonDirectoryChar;
        foreach (char personCharacter in personCharacters)
        {
            a2PeopleSingletonDirectoryChar = Path.Combine(a2PeopleSingletonDirectory, personCharacter.ToString());
            if (!Directory.Exists(a2PeopleSingletonDirectoryChar))
                _ = Directory.CreateDirectory(a2PeopleSingletonDirectoryChar);
        }
        string[] groupDirectories = Directory.GetDirectories(a2PeopleSingletonDirectory, "*", SearchOption.TopDirectoryOnly);
        if (groupDirectories.Length == 0)
            results = [];
        else
            results = GetPersonContainersGroups(personBirthdayFormat, facesFileNameExtension, personCharacters, propertyConfiguration, groupDirectories);
        return results;
    }

    internal static List<long> GetPersonKeys(ReadOnlyCollection<Models.PersonContainer> personContainers)
    {
        List<long> results = [];
        long personKey;
        foreach (Models.PersonContainer personContainer in personContainers)
        {
            if (personContainer.Key is null || personContainer.Birthdays is null || personContainer.Birthdays.Length == 0)
                continue;
            foreach (Models.PersonBirthday personBirthday in personContainer.Birthdays)
            {
                personKey = personBirthday.Value.Ticks;
                results.Add(personKey);
            }
        }
        return results;
    }

}