namespace View_by_Distance.Shared.Models.Stateless.Methods;

internal abstract class XPath
{

    internal static string GetRelativePath(string path, int length, bool forceExtensionToLower)
    {
        string result;
        if (forceExtensionToLower)
        {
            string extension = Path.GetExtension(path);
            string extensionLowered = Path.GetExtension(path).ToLower();
            if (extension != extensionLowered)
            {
                string? directoryName = Path.GetDirectoryName(path);
                if (string.IsNullOrEmpty(directoryName))
                    throw new NullReferenceException(directoryName);
                string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(path);
                if (string.IsNullOrEmpty(fileNameWithoutExtension))
                    throw new NullReferenceException(fileNameWithoutExtension);
                path = Path.Combine(directoryName, $"{fileNameWithoutExtension}{extensionLowered}");
            }
        }
        result = path[length..].Replace(@"\", "/");
        return result;
    }

    internal static bool DeleteEmptyDirectories(string rootDirectory)
    {
        bool result;
        List<string> results = new();
        DeleteEmptyDirectories(rootDirectory, results);
        result = results.Any();
        return result;
    }

    internal static void DeleteEmptyDirectories(string rootDirectory, List<string> deletedDirectories)
    {
        if (Directory.Exists(rootDirectory))
        {
            string[] files;
            string[] directories = Directory.GetDirectories(rootDirectory, "*", SearchOption.TopDirectoryOnly);
            if (directories.Length > 0)
                files = Array.Empty<string>();
            else
                files = Directory.GetFiles(rootDirectory, "*", SearchOption.AllDirectories);
            if (directories.Length == 0 && files.Length == 0)
            {
                deletedDirectories.Add(rootDirectory);
                Directory.Delete(rootDirectory);
            }
            else
            {
                List<string> check = new();
                foreach (string directory in directories)
                {
                    DeleteEmptyDirectories(directory, check);
                    deletedDirectories.AddRange(check);
                    if (check.Any())
                        DeleteEmptyDirectories(directory, deletedDirectories);
                }
            }
        }
    }

    internal static bool WriteAllText(string path, string contents, bool updateDateWhenMatches, bool compareBeforeWrite, DateTime? updateToWhenMatches)
    {
        bool result;
        string text;
        if (!compareBeforeWrite)
            result = true;
        else
        {
            if (!File.Exists(path))
                text = string.Empty;
            else
                text = File.ReadAllText(path);
            result = text != contents;
            if (!result && updateDateWhenMatches)
            {
                if (updateToWhenMatches is null)
                    File.SetLastWriteTime(path, DateTime.Now);
                else
                    File.SetLastWriteTime(path, updateToWhenMatches.Value);
            }
        }
        if (result)
        {
            if (path.Contains("()"))
                File.WriteAllText(path, contents);
            else if (path.Contains("{}") && !path.EndsWith(".json"))
                File.WriteAllText(path, contents);
            else if (path.Contains("[]") && !path.EndsWith(".json"))
                File.WriteAllText(path, contents);
            else if (path.Contains("{}") && path.EndsWith(".json") && contents[0] == '{')
                File.WriteAllText(path, contents);
            else if (path.Contains("[]") && path.EndsWith(".json") && contents[0] == '[')
                File.WriteAllText(path, contents);
            else
                File.WriteAllText(path, contents);
        }
        return result;
    }

    internal static List<string> GetDirectoryNames(string directory)
    {
        List<string> results = new();
        string? fileName;
        string? checkDirectory = directory;
        string? pathRoot = Path.GetPathRoot(directory);
        string extension = Path.GetExtension(directory);
        if (string.IsNullOrEmpty(pathRoot))
            throw new NullReferenceException(nameof(pathRoot));
        if (Directory.Exists(directory))
        {
            fileName = Path.GetFileName(directory);
            if (!string.IsNullOrEmpty(fileName))
                results.Add(fileName);
        }
        else if ((string.IsNullOrEmpty(extension) || extension.Length > 3) && !File.Exists(directory))
        {
            fileName = Path.GetFileName(directory);
            if (!string.IsNullOrEmpty(fileName))
                results.Add(fileName);
        }
        for (int i = 0; i < int.MaxValue; i++)
        {
            checkDirectory = Path.GetDirectoryName(checkDirectory);
            if (string.IsNullOrEmpty(checkDirectory) || checkDirectory == pathRoot)
                break;
            fileName = Path.GetFileName(checkDirectory);
            if (string.IsNullOrEmpty(fileName))
                continue;
            results.Add(fileName);
        }
        results.Add(pathRoot);
        results.Reverse();
        return results;
    }

    internal static List<string> GetDirectories(string directory)
    {
        List<string> results = new();
        string? checkDirectory = directory;
        string? pathRoot = Path.GetPathRoot(directory);
        if (string.IsNullOrEmpty(pathRoot))
            throw new NullReferenceException(nameof(pathRoot));
        if (Directory.Exists(directory))
            results.Add(directory);
        for (int i = 0; i < int.MaxValue; i++)
        {
            checkDirectory = Path.GetDirectoryName(checkDirectory);
            if (string.IsNullOrEmpty(checkDirectory) || checkDirectory == pathRoot)
                break;
            results.Add(checkDirectory);
        }
        results.Add(pathRoot);
        results.Reverse();
        return results;
    }

    internal static (int level, List<string> directories) Get(string rootDirectory, string sourceDirectory)
    {
        int result = 0;
        string? directory;
        string? checkDirectory;
        List<string> results = new();
        checkDirectory = sourceDirectory;
        for (int i = 0; i < int.MaxValue; i++)
        {
            result += 1;
            directory = Path.GetFileName(checkDirectory);
            if (string.IsNullOrEmpty(directory))
                break;
            results.Add(directory);
            checkDirectory = Path.GetDirectoryName(checkDirectory);
            if (checkDirectory == rootDirectory)
                break;
        }
        results.Reverse();
        return new(result, results);
    }

    internal static string GetDirectory(string sourceDirectory, int level, string directoryName)
    {
        string result;
        string? checkDirectory;
        checkDirectory = Path.GetDirectoryName(sourceDirectory);
        for (int i = 0; i < level; i++)
            checkDirectory = Path.GetDirectoryName(checkDirectory);
        if (string.IsNullOrEmpty(checkDirectory))
            throw new Exception();
        checkDirectory = Path.Combine(checkDirectory, directoryName);
        if (!Directory.Exists(checkDirectory))
            _ = Directory.CreateDirectory(checkDirectory);
        result = checkDirectory;
        return result;
    }

    internal static void ChangeDateForEmptyDirectories(string rootDirectory, long ticks)
    {
        DateTime dateTime = new(ticks);
        IEnumerable<string> fileSystemEntries;
        string[] directories;
        if (!Directory.Exists(rootDirectory))
            directories = Array.Empty<string>();
        else
            directories = Directory.GetDirectories(rootDirectory, "*", SearchOption.AllDirectories);
        foreach (string directory in directories)
        {
            fileSystemEntries = Directory.EnumerateFileSystemEntries(directory, "*", SearchOption.TopDirectoryOnly);
            if (fileSystemEntries.Any())
                continue;
            Directory.SetLastWriteTime(directory, dateTime);
        }
    }

    internal static void MakeHiddenIfAllItemsAreHidden(string rootDirectory)
    {
        bool check;
        FileInfo fileInfo;
        IEnumerable<string> files;
        DirectoryInfo directoryInfo;
        IEnumerable<string> subDirectories;
        string[] directories = Directory.GetDirectories(rootDirectory, "*", SearchOption.AllDirectories);
        foreach (string directory in directories)
        {
            directoryInfo = new(directory);
            if (directoryInfo.Attributes.HasFlag(FileAttributes.Hidden))
                continue;
            check = true;
            subDirectories = Directory.EnumerateDirectories(directory, "*", SearchOption.TopDirectoryOnly);
            foreach (string subDirectory in subDirectories)
            {
                directoryInfo = new(subDirectory);
                if (!directoryInfo.Attributes.HasFlag(FileAttributes.Hidden))
                {
                    check = false;
                    break;
                }
            }
            if (!check)
                continue;
            files = Directory.EnumerateFiles(directory, "*", SearchOption.TopDirectoryOnly);
            foreach (string file in files)
            {
                fileInfo = new(file);
                if (!fileInfo.Attributes.HasFlag(FileAttributes.Hidden))
                {
                    check = false;
                    break;
                }
            }
            if (!check)
                continue;
            directoryInfo.Attributes |= FileAttributes.Hidden;
        }
    }

    internal static Dictionary<string, string[]> GetKeyValuePairs(string resultAllInOne, string? resultsFullGroupDirectory, string[]? directories, int maxValue = 12)
    {
        Dictionary<string, string[]> results = new();
        string checkDirectory;
        int minusOne = maxValue - 1;
        int minusTwo = maxValue - 2;
        int minusThree = maxValue - 3;
        List<string> collection = new();
        int length = minusThree.ToString().Length;
        if (directories is not null)
        {
            foreach (string key in directories)
            {
                if (resultsFullGroupDirectory is null)
                    continue;
                collection.Clear();
                for (int i = 0; i < maxValue; i++)
                {
                    if (i == minusTwo)
                        checkDirectory = Path.Combine(resultsFullGroupDirectory, key, resultAllInOne, new('-', length));
                    else if (i == minusOne)
                        checkDirectory = Path.Combine(resultsFullGroupDirectory, key, resultAllInOne, new('_', length));
                    else
                        checkDirectory = Path.Combine(resultsFullGroupDirectory, key, resultAllInOne, i.ToString().PadLeft(length, '0'));
                    if (!Directory.Exists(checkDirectory))
                        _ = Directory.CreateDirectory(checkDirectory);
                    collection.Add(checkDirectory);
                }
                results.Add(key, collection.ToArray());
            }
        }
        return results;
    }

}