using File_Folder_Helper.Helpers;
using Microsoft.Extensions.Logging;
using System.Collections.ObjectModel;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace File_Folder_Helper.ADO2024.PI2;

internal static partial class Helper20240520
{

    private record RecordA(string Directory, string Extension, string SourceFile, Identifier Identifier);

    private record RecordB(ReadOnlyDictionary<int, Identifier> IdTo, ReadOnlyDictionary<long, Identifier> LengthTo, ReadOnlyDictionary<string, Identifier> PaddedTo);

    internal sealed record Identifier(int Id, long Length, string PaddedId, long Ticks)
    {

        public override string ToString()
        {
            string result = JsonSerializer.Serialize(this, IdentifierSourceGenerationContext.Default.Identifier);
            return result;
        }

    }

    [JsonSourceGenerationOptions(WriteIndented = true)]
    [JsonSerializable(typeof(Identifier))]
    internal partial class IdentifierSourceGenerationContext : JsonSerializerContext
    {
    }

    [JsonSourceGenerationOptions(WriteIndented = true)]
    [JsonSerializable(typeof(Identifier[]))]
    internal partial class IdentifierCollectionSourceGenerationContext : JsonSerializerContext
    {
    }

    private static RecordB GetRecordB(string jsonFile)
    {
        RecordB result;
        Dictionary<int, Identifier> idTo = [];
        Dictionary<long, Identifier> lengthTo = [];
        Dictionary<string, Identifier> paddedTo = [];
        string? json = !File.Exists(jsonFile) ? null : File.ReadAllText(jsonFile);
        Identifier[]? identifiers = json is null ? null : JsonSerializer.Deserialize(json, IdentifierCollectionSourceGenerationContext.Default.IdentifierArray);
        if (identifiers is null && !string.IsNullOrEmpty(jsonFile))
            throw new Exception($"Invalid {nameof(jsonFile)}");
        if (identifiers is not null)
        {
            foreach (Identifier identifier in identifiers)
            {
                idTo.Add(identifier.Id, identifier);
                paddedTo.Add(identifier.PaddedId, identifier);
                if (lengthTo.ContainsKey(identifier.Length))
                {
                    _ = lengthTo.Remove(identifier.Length);
                    continue;
                }
                lengthTo.Add(identifier.Length, identifier);
            }
        }
        result = new(new(idTo), new(lengthTo), new(paddedTo));
        return result;
    }

    internal static void IdentifierRename(ILogger<Worker> logger, List<string> args)
    {
        int id;
        string key;
        RecordA recordA;
        string checkFile;
        FileInfo fileInfo;
        string checkDirectory;
        Identifier? identifier;
        string offset = args[5];
        string option = args[7];
        string jsonFile = args[4];
        string fileNameWithoutExtension;
        List<RecordA> recordACollection = [];
        RecordB recordB = GetRecordB(jsonFile);
        string deterministicHashCode = args[3];
        string source = Path.GetFullPath(args[0]);
        int intMinValueLength = int.Parse(args[2]);
        string destination = Path.GetFullPath(args[6]);
        bool isOffsetDeterministicHashCode = offset == deterministicHashCode;
        string[] sourceFiles = Directory.GetFiles(source, "*", SearchOption.AllDirectories);
        logger.LogInformation("Found {files}(s)", sourceFiles.Length);
        int sourceLength = source.Length;
        foreach (string sourceFile in sourceFiles)
        {
            fileInfo = new(sourceFile);
            fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileInfo.FullName);
            if (fileInfo.Directory is null)
                throw new NotSupportedException();
            if (option == "Padded")
            {
                if (fileNameWithoutExtension.Length < intMinValueLength)
                    continue;
                key = fileNameWithoutExtension;
                if (recordB.PaddedTo.TryGetValue(key, out identifier))
                {
                    recordACollection.Add(new($"{destination}{fileInfo.Directory.FullName[sourceLength..]}", fileInfo.Extension, fileInfo.FullName, identifier));
                    continue;
                }
            }
            if (option == "Length")
            {
                if (recordB.LengthTo.TryGetValue(fileInfo.Length, out identifier))
                {
                    checkDirectory = $"{destination}{fileInfo.Directory.FullName[sourceLength..]}";
                    if (!Directory.Exists(checkDirectory))
                        _ = Directory.CreateDirectory(checkDirectory);
                    checkFile = Path.Combine(checkDirectory, $"{identifier.PaddedId}{fileInfo.Extension}");
                    if (File.Exists(checkFile))
                        continue;
                    File.Copy(fileInfo.FullName, checkFile);
                    logger.LogInformation("<{fileInfo.FullName}> was moved to <{checkFile}>", fileInfo.FullName, checkFile);
                    continue;
                }
            }
            if (option == "Id")
            {
                if (int.TryParse(fileNameWithoutExtension, out id))
                {
                    if (recordB.IdTo.TryGetValue(id, out identifier))
                    {
                        checkDirectory = $"{destination}{fileInfo.Directory.FullName[sourceLength..]}";
                        if (!Directory.Exists(checkDirectory))
                            _ = Directory.CreateDirectory(checkDirectory);
                        checkFile = Path.Combine(checkDirectory, $"{identifier.PaddedId}{fileInfo.Extension}");
                        if (File.Exists(checkFile))
                            continue;
                        File.Move(fileInfo.FullName, checkFile);
                        logger.LogInformation("<{fileInfo.FullName}> was moved to <{checkFile}>", fileInfo.FullName, checkFile);
                        continue;
                    }
                }
            }
        }
        if (option == "Padded")
        {
            if (!isOffsetDeterministicHashCode)
                recordACollection = (from l in recordACollection orderby l.Identifier.Ticks select l).ToList();
            for (int i = 0; i < recordACollection.Count; i++)
            {
                recordA = recordACollection[i];
                if (!Directory.Exists(recordA.Directory))
                    _ = Directory.CreateDirectory(recordA.Directory);
                checkFile = Path.Combine(recordA.Directory, isOffsetDeterministicHashCode ? $"{recordA.Identifier.PaddedId}{recordA.Extension}" : $"{offset + i}{recordA.Identifier.PaddedId}{recordA.Extension}");
                if (File.Exists(checkFile))
                    continue;
                File.Move(recordA.SourceFile, checkFile);
                logger.LogInformation("<{recordA.SourceFile}> was moved to <{checkFile}>", recordA.SourceFile, checkFile);
            }
        }
        HelperDeleteEmptyDirectories.DeleteEmptyDirectories(logger, source);
    }

}