using System.Text.Json;
using View_by_Distance.Instance.Models.Stateless;
using View_by_Distance.Metadata.Models;
using View_by_Distance.Resize.Models;
using View_by_Distance.Shared.Models;
using View_by_Distance.Shared.Models.Methods;
using View_by_Distance.Shared.Models.Stateless;

namespace View_by_Distance.Instance.Models;

/// <summary>
// N/A
/// </summary>
internal class E2_Navigate
{

    private readonly string _ArgZero;
    private readonly E3_Rename _Rename;
    private readonly IConsole _Console;
    private readonly Serilog.ILogger? _Log;
    private readonly Configuration _Configuration;

    internal E2_Navigate(IConsole console, Configuration configuration, string argZero)
    {
        _Console = console;
        _ArgZero = argZero;
        _Configuration = configuration;
        _Rename = new E3_Rename(configuration);
        _Log = Serilog.Log.ForContext<E2_Navigate>();
    }

    public override string ToString()
    {
        string result = JsonSerializer.Serialize(this, new JsonSerializerOptions() { WriteIndented = true });
        return result;
    }

    private void DisplayTags(Property.Models.Configuration configuration, Model? model, PredictorModel? predictorModel, string outputResolution, string[] directories, Dictionary<ConsoleKey, int> directoryKeyValuePairs, string[] files, Dictionary<ConsoleKey, int> fileKeyValuePairs)
    {
        if (_Log is null)
            throw new Exception($"{nameof(_Log)} is null!");
        bool all = false;
        FileSystem fileSystem;
        string requestPath = "/RootResultsDirectory";
        string? rootResultsDirectory = Path.GetDirectoryName(Property.Models.Stateless.IResult.GetResultsGroupDirectory(configuration, nameof(B_Metadata)));
        if (string.IsNullOrEmpty(rootResultsDirectory))
            throw new Exception();
        string rootResultsDirectoryAbsoluteUri = new Uri(rootResultsDirectory).AbsoluteUri;
        string dFacesContentDirectory = Path.Combine(Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(configuration, model, predictorModel, nameof(D_Face), outputResolution, includeResizeGroup: true, includeModel: true, includePredictorModel: true), "()");
        string cResizeContentDirectory = Path.Combine(Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(configuration, model, predictorModel, nameof(C_Resize), outputResolution, includeResizeGroup: true, includeModel: false, includePredictorModel: false), "()");
        string eDistanceCollectionDirectory = Path.Combine(Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(configuration, model, predictorModel, nameof(E_Distance), outputResolution, includeResizeGroup: true, includeModel: true, includePredictorModel: true), "[]");
        (string RootResultsDirectoryAbsoluteUri, string C_ResizeContentDirectory, string D_FacesContentDirectory, string E_DistanceCollectionDirectory) tuple = new(rootResultsDirectoryAbsoluteUri, cResizeContentDirectory, dFacesContentDirectory, eDistanceCollectionDirectory);
        List<FileSystem> fileSystemCollection = Shared.Models.Stateless.Methods.IFileSystem.GetFileSystemCollection(requestPath, tuple, directories, files, all);
        Queue<FileSystem> queue = new(fileSystemCollection);
        foreach (KeyValuePair<ConsoleKey, int> element in directoryKeyValuePairs)
        {
            fileSystem = queue.Dequeue();
            _Log.Warn(string.Concat(element.Key, " - D) ", " <", fileSystem.Display, ">"));
            // _Log.Info(string.Join(Environment.NewLine, from l in fileSystem where !string.IsNullOrEmpty(l) select l));
            _Log.Info(string.Empty);
        }
        foreach (KeyValuePair<ConsoleKey, int> element in fileKeyValuePairs)
        {
            fileSystem = queue.Dequeue();
            _Log.Warn(string.Concat(element.Key, " - F) [", fileSystem.Display, '}'));
            // _Log.Info(string.Join(Environment.NewLine, from l in fileSystem where !string.IsNullOrEmpty(l) select l));
            _Log.Info(string.Empty);
        }
    }

    private void DisplayFaces(Property.Models.Configuration configuration, Model? model, PredictorModel? predictorModel, string outputResolution, string selectedFileFullName)
    {
        if (_Log is null)
            throw new Exception($"{nameof(_Log)} is null!");
        string requestPath = "/RootResultsDirectory";
        string? rootResultsDirectory = Path.GetDirectoryName(Property.Models.Stateless.IResult.GetResultsGroupDirectory(configuration, nameof(B_Metadata)));
        if (string.IsNullOrEmpty(rootResultsDirectory))
            throw new Exception();
        string rootResultsDirectoryAbsoluteUri = new Uri(rootResultsDirectory).AbsoluteUri;
        string dFacesContentDirectory = Path.Combine(Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(configuration, model, predictorModel, nameof(D_Face), outputResolution, includeResizeGroup: true, includeModel: true, includePredictorModel: true), "()");
        string cResizeContentDirectory = Path.Combine(Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(configuration, model, predictorModel, nameof(C_Resize), outputResolution, includeResizeGroup: true, includeModel: false, includePredictorModel: false), "()");
        string eDistanceCollectionDirectory = Path.Combine(Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(configuration, model, predictorModel, nameof(E_Distance), outputResolution, includeResizeGroup: true, includeModel: true, includePredictorModel: true), "[]");
        (string RootResultsDirectoryAbsoluteUri, string C_ResizeContentDirectory, string D_FacesContentDirectory, string E_DistanceCollectionDirectory) tuple = new(rootResultsDirectoryAbsoluteUri, cResizeContentDirectory, dFacesContentDirectory, eDistanceCollectionDirectory);
        FaceFileSystem[] faceFileSystemCollection = Shared.Models.Stateless.Methods.IFaceFileSystem.GetFaceFileSystemCollection(requestPath, tuple, selectedFileFullName);
        for (int i = 0; i < faceFileSystemCollection.Length; i++)
        {
            _Log.Warn(string.Concat(i, " - F) [", faceFileSystemCollection[i].Display, '}'));
            // _Log.Info(string.Join(Environment.NewLine, from l in fileSystemCollection[i] where !string.IsNullOrEmpty(l) select l));
            _Log.Info(string.Empty);
        }
        // }
    }

    private string Rename(Property.Models.Configuration configuration, Model? model, PredictorModel? predictorModel, string subSourceDirectory)
    {
        string result;
        if (_Log is null)
            throw new Exception($"{nameof(_Log)} is null!");
        if (_Configuration?.PropertyConfiguration is null)
            throw new Exception($"{nameof(_Configuration.PropertyConfiguration)} must be set!");
        _Log.Warn(string.Concat("What is the new name for [", Path.GetFileName(subSourceDirectory), "]<", subSourceDirectory, ">?"));
        string? newDirectoryName = _Console.ReadLine();
        _Log.Warn("Are you sure y[es] || n[o]?");
        if (string.IsNullOrEmpty(newDirectoryName) || _Console.ReadKey() != ConsoleKey.Y)
        {
            _Log.Warn(string.Empty);
            _Log.Warn("No changes made.");
            result = subSourceDirectory;
        }
        else
        {
            _Log.Warn(string.Empty);
            string eDistanceCollectionDirectory = Path.Combine(Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(configuration, model, predictorModel, nameof(E_Distance), _Configuration.ValidResolutions[0], includeResizeGroup: true, includeModel: true, includePredictorModel: true), "[]");
            string relativePath = Property.Models.Stateless.IPath.GetRelativePath(subSourceDirectory, eDistanceCollectionDirectory.Length);
            if (relativePath.Length == 1)
                throw new Exception();
            if (Directory.Exists(Path.Combine(string.Concat(_Configuration.PropertyConfiguration.RootDirectory, Path.GetDirectoryName(relativePath)), newDirectoryName)))
            {
                _Log.Warn("\"To\" directory already exits!");
                result = subSourceDirectory;
            }
            else
            {
                _Rename.DirectoryRename(configuration, model, predictorModel, relativePath, newDirectoryName);
                _Log.Warn("Renamed...");
                string? directoryName = Path.GetDirectoryName(subSourceDirectory);
                if (string.IsNullOrEmpty(directoryName))
                    throw new Exception();
                result = Path.Combine(directoryName, newDirectoryName);
            }
        }
        return result;
    }

    internal void Navigate(Property.Models.Configuration configuration, Model? model, PredictorModel? predictorModel, string outputResolution)
    {
        if (_Log is null)
            throw new Exception($"{nameof(_Log)} is null!");
        string[] subFiles;
        ConsoleKey consoleKey;
        string[] subDirectories;
        string selectedFileFullName;
        string rootDirectory = string.Empty;
        string? subSourceDirectory = string.Empty;
        Dictionary<ConsoleKey, int> fileKeyValuePairs = new();
        Dictionary<ConsoleKey, int> directoryKeyValuePairs = new();
        string eDistanceCollectionDirectory = Path.Combine(Property.Models.Stateless.IResult.GetResultsFullGroupDirectory(configuration, model, predictorModel, nameof(E_Distance), _Configuration.OutputResolutions[0], includeResizeGroup: true, includeModel: true, includePredictorModel: true), "[]");
        if (!Directory.Exists(eDistanceCollectionDirectory))
            _ = Directory.CreateDirectory(eDistanceCollectionDirectory);
        for (int z = 0; z < int.MaxValue; z++)
        {
            if (string.IsNullOrEmpty(subSourceDirectory))
            {
                rootDirectory = eDistanceCollectionDirectory;
                subSourceDirectory = rootDirectory;
            }
            subFiles = Directory.GetFiles(subSourceDirectory, "*.json", SearchOption.TopDirectoryOnly);
            subDirectories = Directory.GetDirectories(subSourceDirectory, "*", SearchOption.TopDirectoryOnly);
            directoryKeyValuePairs.Clear();
            for (int i = (int)ConsoleKey.A; i < (subDirectories.Length + (int)ConsoleKey.A) && i <= (int)ConsoleKey.RightWindows; i++)
                directoryKeyValuePairs.Add((ConsoleKey)i, i - (int)ConsoleKey.A);
            fileKeyValuePairs.Clear();
            for (int i = (int)ConsoleKey.A + subDirectories.Length; i < (subFiles.Length + (int)ConsoleKey.A + subDirectories.Length) && i <= (int)ConsoleKey.RightWindows; i++)
                fileKeyValuePairs.Add((ConsoleKey)i, i - (int)ConsoleKey.A);
            if (!directoryKeyValuePairs.Any() && !fileKeyValuePairs.Any())
                break;
            _Log.Warn("");
            DisplayTags(configuration, model, predictorModel, outputResolution, subDirectories, directoryKeyValuePairs, subFiles, fileKeyValuePairs);
            _Log.Warn(string.Empty);
            _Log.Warn(string.Empty);
            _Log.Warn(string.Empty);
            _Log.Warn("Select a file system object. Enter \"Backspace\" to go up a directory, \"F2\" to rename and \"Esc\" to end navigation.");
            consoleKey = _Console.ReadKey();
            _Log.Warn(string.Empty);
            if (consoleKey == ConsoleKey.Escape)
                break;
            if (consoleKey == ConsoleKey.F2)
            {
                if (subSourceDirectory == rootDirectory)
                {
                    _Log.Warn("Root directory can not be renamed! Try again.");
                    continue;
                }
                else
                {
                    subSourceDirectory = Rename(configuration, model, predictorModel, subSourceDirectory);
                    continue;
                }
            }
            else if (consoleKey is ConsoleKey.Backspace or ConsoleKey.LeftArrow)
            {
                if (subSourceDirectory != rootDirectory)
                    subSourceDirectory = Path.GetDirectoryName(subSourceDirectory);
                else
                {
                    _Log.Warn("At root directory. Try again.");
                    continue;
                }
            }
            else if (directoryKeyValuePairs.ContainsKey(consoleKey))
            {
                subSourceDirectory = subDirectories[directoryKeyValuePairs[consoleKey]];
                _Log.Warn(string.Concat(">>> [", Path.GetFileName(subSourceDirectory), "]<", subSourceDirectory, ">?"));
                continue;
            }
            else
            {
                if (!fileKeyValuePairs.ContainsKey(consoleKey))
                {
                    subSourceDirectory = _ArgZero;
                    _Log.Warn("Invalid selection. Try again.");
                    continue;
                }
                else
                {
                    selectedFileFullName = subFiles[fileKeyValuePairs[consoleKey]];
                    _Log.Warn(string.Concat(">>> [", Path.GetFileName(selectedFileFullName), "]<", selectedFileFullName, ">?"));
                    DisplayFaces(configuration, model, predictorModel, outputResolution, selectedFileFullName);
                    _Log.Warn(string.Empty);
                    _Log.Warn(string.Empty);
                    _Log.Warn(string.Empty);
                    _Log.Warn("Go up a directory? Enter escape end.");
                    consoleKey = _Console.ReadKey();
                    if (consoleKey == ConsoleKey.Escape)
                        break;
                    subSourceDirectory = Path.GetDirectoryName(subSourceDirectory);
                    continue;
                }
            }
        }
    }

}