namespace View_by_Distance.Shared.Models.Stateless.Methods;

internal abstract class PersonContainer
{

    private static string[] GetFiles(string facesFileNameExtension, string personDisplayDirectory)
    {
        string[] results = Directory.GetFiles(personDisplayDirectory, "*", SearchOption.TopDirectoryOnly);
        int? id;
        string checkFile;
        string? checkDirectory;
        int? normalizedRectangle;
        foreach (string personDisplayDirectoryAllFile in results)
        {
            if (personDisplayDirectoryAllFile.EndsWith(".lnk"))
                continue;
            (id, normalizedRectangle) = IMapping.GetConverted(facesFileNameExtension, personDisplayDirectoryAllFile);
            if (id is not null && normalizedRectangle is not null && !personDisplayDirectoryAllFile.EndsWith(".json"))
                continue;
            checkDirectory = Path.GetDirectoryName(personDisplayDirectoryAllFile);
            if (string.IsNullOrEmpty(checkDirectory))
                continue;
            checkDirectory = Path.Combine(checkDirectory, "_ Invalid");
            if (!Directory.Exists(checkDirectory))
                _ = Directory.CreateDirectory(checkDirectory);
            checkFile = Path.Combine(checkDirectory, Path.GetFileName(personDisplayDirectoryAllFile));
            if (File.Exists(checkFile))
                File.Delete(personDisplayDirectoryAllFile);
            else
                File.Move(personDisplayDirectoryAllFile, checkFile);
        }
        results = Directory.GetFiles(personDisplayDirectory, "*", SearchOption.AllDirectories);
        return results;
    }

    private static List<(long?, Models.PersonContainer)> GetPersonContainersCollections(string facesFileNameExtension, char[] chars, char numberSign, string personDisplayDirectory, string personDisplayDirectoryName, int? approximateYears, List<(string PersonKeyFormatted, Models.PersonBirthday PersonBirthday)> collection)
    {
        List<(long?, Models.PersonContainer)> results = new();
        long personKey;
        string[] segments;
        const int zero = 0;
        Models.Person person;
        string? partialPersonKeyFormatted;
        Models.PersonContainer personContainer;
        Models.PersonBirthday[] orderedPersonBirthdays;
        IEnumerable<string> partialPersonKeyFormattedCollection;
        string[] personDisplayDirectoryAllFiles = GetFiles(facesFileNameExtension, personDisplayDirectory);
        foreach ((string personKeyFormatted, Models.PersonBirthday personBirthday) in collection)
        {
            segments = personDisplayDirectoryName.Split(chars);
            orderedPersonBirthdays = (from l in collection where !l.PersonKeyFormatted.Contains(numberSign) orderby l.PersonBirthday.Value.Ticks descending select l.PersonBirthday).ToArray();
            if (!orderedPersonBirthdays.Any())
                personKey = collection[zero].PersonBirthday.Value.Ticks;
            else
            {
                if (personKeyFormatted.Contains(numberSign))
                    continue;
                personKey = orderedPersonBirthdays[zero].Value.Ticks;
            }
            person = IPerson.GetPerson(personKey, segments);
            partialPersonKeyFormattedCollection = (from l in collection where l.PersonKeyFormatted.Contains(numberSign) select l.PersonKeyFormatted);
            partialPersonKeyFormatted = partialPersonKeyFormattedCollection.FirstOrDefault();
            personContainer = new(approximateYears, person, orderedPersonBirthdays, personDisplayDirectoryAllFiles, personDisplayDirectoryName, personKey, partialPersonKeyFormatted);
            results.Add(new(personKey, personContainer));
        }
        return results;
    }

    private static Models.PersonContainer GetPersonContainer(string facesFileNameExtension, string personDisplayDirectory, string personDisplayDirectoryName, int? approximateYears)
    {
        Models.PersonContainer result;
        string[] personDisplayDirectoryAllFiles = GetFiles(facesFileNameExtension, personDisplayDirectory);
        result = new(approximateYears, personDisplayDirectoryAllFiles, personDisplayDirectoryName);
        return result;
    }

    private 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.Any())
            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 < -356)
            updateApproximateYears = approximateYears.Value + 1;
        else if (timeSpan.TotalDays > 356)
            updateApproximateYears = approximateYears.Value - 1;
        else
            updateApproximateYears = null;
        if (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<(long?, Models.PersonContainer)> GetPersonContainersGroup(string personBirthdayFormat, string facesFileNameExtension, char[] chars, string[] personDisplayDirectories)
    {
        List<(long?, Models.PersonContainer)> results = new();
        string? minusOne;
        int? approximateYears;
        char numberSign = '#';
        List<string?> changes = new();
        string[] personKeyDirectories;
        string? personDisplayDirectoryName;
        Models.PersonContainer personContainer;
        List<(string, Models.PersonBirthday)> collection;
        foreach (string personDisplayDirectory in personDisplayDirectories)
        {
            personDisplayDirectoryName = Path.GetFileName(personDisplayDirectory);
            if (string.IsNullOrEmpty(personDisplayDirectoryName))
                continue;
            approximateYears = Age.GetApproximateYears(personDisplayDirectoryName, chars);
            personKeyDirectories = Directory.GetDirectories(personDisplayDirectory, "*", SearchOption.TopDirectoryOnly);
            collection = PersonBirthday.GetPersonBirthdays(personBirthdayFormat, personKeyDirectories, personDisplayDirectory, 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.Any())
                results.AddRange(GetPersonContainersCollections(facesFileNameExtension, chars, numberSign, personDisplayDirectory, personDisplayDirectoryName, approximateYears, collection));
            else
            {
                personContainer = GetPersonContainer(facesFileNameExtension, personDisplayDirectory, personDisplayDirectoryName, approximateYears);
                results.Add(new(null, personContainer));
            }
        }
        if (changes.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 changes where l is not null select l).ToArray())}");
        return results;
    }

    private static Models.PersonContainer[] GetPersonContainersGroups(string personBirthdayFormat, string facesFileNameExtension, char[] chars, string[] groupDirectories)
    {
        Models.PersonContainer[] results;
        const int zero = 0;
        string groupDirectoryName;
        string[] personDisplayDirectories;
        List<(long?, Models.PersonContainer)> collection;
        List<(long? PersonKey, Models.PersonContainer PersonContainer)> personContainers = new();
        foreach (string groupDirectory in groupDirectories)
        {
            groupDirectoryName = Path.GetFileName(groupDirectory);
            if (!chars.Contains(groupDirectoryName[zero]))
                continue;
            personDisplayDirectories = Directory.GetDirectories(groupDirectory, "*", SearchOption.TopDirectoryOnly);
            collection = GetPersonContainersGroup(personBirthdayFormat, facesFileNameExtension, chars, personDisplayDirectories);
            personContainers.AddRange(collection);
        }
        results = (from l in personContainers orderby l.PersonKey is not null, l.PersonKey select l.PersonContainer).ToArray();
        return results;
    }

    internal static Models.PersonContainer[] GetPersonContainers(Properties.IStorage storage, string personBirthdayFormat, string facesFileNameExtension)
    {
        Models.PersonContainer[] results;
        char[] chars = IAge.GetChars();
        string a2PeopleSingletonDirectory = Path.Combine(storage.PeopleRootDirectory, "{}");
        if (!Directory.Exists(a2PeopleSingletonDirectory))
            _ = Directory.CreateDirectory(a2PeopleSingletonDirectory);
        string a2PeopleSingletonDirectoryChar;
        foreach (char @char in chars)
        {
            a2PeopleSingletonDirectoryChar = Path.Combine(a2PeopleSingletonDirectory, @char.ToString());
            if (!Directory.Exists(a2PeopleSingletonDirectoryChar))
                _ = Directory.CreateDirectory(a2PeopleSingletonDirectoryChar);
        }
        string[] groupDirectories = Directory.GetDirectories(a2PeopleSingletonDirectory, "*", SearchOption.TopDirectoryOnly);
        if (!groupDirectories.Any())
            results = Array.Empty<Models.PersonContainer>();
        else
            results = GetPersonContainersGroups(personBirthdayFormat, facesFileNameExtension, chars, groupDirectories);
        return results;
    }

    internal static List<(long?, string)> GetDisplay(string personBirthdayFormat, Models.PersonContainer personContainer)
    {
        List<(long?, string)> results = new();
        string personKeyFormatted;
        Models.PersonContainer pc = personContainer;
        if (pc.Person is null || pc.Key is null || pc.Birthdays is null || !pc.Birthdays.Any())
            results.Add(new(pc.Key, string.Concat('\t', pc.Key, '\t', pc.ApproximateYears, '\t', pc.DisplayDirectoryName)));
        else
        {
            foreach (Models.PersonBirthday personBirthday in pc.Birthdays)
            {
                personKeyFormatted = IPersonBirthday.GetFormatted(personBirthdayFormat, personBirthday);
                results.Add(new(pc.Key, string.Concat(personKeyFormatted, '\t', pc.Key, '\t', pc.ApproximateYears, '\t', pc.DisplayDirectoryName)));
            }
        }
        return results;
    }

}