using File_Watcher.Models;
using System.IO.Compression;

namespace File_Watcher.Helpers;

internal class HelperNuget
{

    private static DateTimeOffset? GetDateTimeOffset(string keyFileExtension, FileInfo fileInfo, FileInfo extractKeyFileInfo)
    {
        DateTimeOffset? dateTimeOffset = null;
        using ZipArchive zip = ZipFile.Open(fileInfo.FullName, ZipArchiveMode.Read);
        foreach (ZipArchiveEntry zipArchiveEntry in zip.Entries)
        {
            if (!zipArchiveEntry.Name.EndsWith(keyFileExtension))
                continue;
            dateTimeOffset = zipArchiveEntry.LastWriteTime;
            if (fileInfo.FullName[0] != '\\')
            {
                zipArchiveEntry.ExtractToFile(extractKeyFileInfo.FullName);
                File.SetCreationTime(extractKeyFileInfo.FullName, fileInfo.CreationTime);
                File.SetLastWriteTime(extractKeyFileInfo.FullName, dateTimeOffset.Value.LocalDateTime);
            }
            break;
        }
        return dateTimeOffset;
    }

    private static void SetLastWriteTimes(ILogger<Worker> logger, NugetConfiguration nugetConfiguration, FileInfo fileInfo, DateTimeOffset dateTimeOffset)
    {
        try
        {
            string[] files;
            if (fileInfo.LastWriteTime != dateTimeOffset.LocalDateTime)
                File.SetLastWriteTime(fileInfo.FullName, dateTimeOffset.LocalDateTime);
            if (!string.IsNullOrEmpty(nugetConfiguration.KeyFileExtensionB))
            {
                if (fileInfo.DirectoryName is null)
                    throw new NullReferenceException(nameof(fileInfo.DirectoryName));
                files = Directory.GetFiles(fileInfo.DirectoryName, nugetConfiguration.KeyFileExtensionB, SearchOption.TopDirectoryOnly);
                foreach (string file in files)
                {
                    fileInfo = new(file);
                    if (fileInfo.LastWriteTime != dateTimeOffset.LocalDateTime)
                        File.SetLastWriteTime(fileInfo.FullName, dateTimeOffset.LocalDateTime);
                }
            }
            if (!string.IsNullOrEmpty(nugetConfiguration.KeyFileExtensionC))
            {
                if (fileInfo.DirectoryName is null)
                    throw new NullReferenceException(nameof(fileInfo.DirectoryName));
                files = Directory.GetFiles(fileInfo.DirectoryName, nugetConfiguration.KeyFileExtensionC, SearchOption.TopDirectoryOnly);
                foreach (string file in files)
                {
                    fileInfo = new(file);
                    if (fileInfo.LastWriteTime != dateTimeOffset.LocalDateTime)
                        File.SetLastWriteTime(fileInfo.FullName, dateTimeOffset.LocalDateTime);
                }
            }
        }
        catch (Exception)
        {
            string checkFile;
            logger.LogInformation("<{fileInfo.FullName}> is invalid!", fileInfo.FullName);
            checkFile = string.Concat(fileInfo.FullName, ".err");
            for (int e = 0; e < short.MaxValue; e++)
            {
                if (!File.Exists(checkFile))
                    break;
                checkFile = string.Concat(checkFile, e);
            }
            try
            { File.Move(fileInfo.FullName, checkFile); }
            catch (Exception) { logger.LogInformation("<{fileInfo.FullName}> couldn't be moved!", fileInfo.FullName); }
        }
    }

    private static void ExtractKeyFileAndSetDateFromZipEntry(ILogger<Worker> logger, NugetConfiguration nugetConfiguration, string[] zipFiles)
    {
        string[] files;
        FileInfo fileInfo;
        string? lowerName;
        FileInfo extractKeyFileInfo;
        foreach (string zipFile in zipFiles)
        {
            fileInfo = new(zipFile);
            if (fileInfo.DirectoryName is null)
                throw new NullReferenceException(nameof(fileInfo.DirectoryName));
            lowerName = !nugetConfiguration.RenameToLower ? null : Path.Combine(fileInfo.DirectoryName, fileInfo.Name.ToLower());
            if (nugetConfiguration.RenameToLower && lowerName is not null && lowerName != fileInfo.FullName)
            {
                files = Directory.GetFiles(fileInfo.DirectoryName, $"{Path.GetFileNameWithoutExtension(fileInfo.Name)}*", SearchOption.TopDirectoryOnly);
                foreach (string file in files)
                    File.Move(file, Path.Combine(fileInfo.DirectoryName, Path.GetFileName(file).ToLower()));
                fileInfo = new(lowerName);
                if (fileInfo.DirectoryName is null)
                    throw new NullReferenceException(nameof(fileInfo.DirectoryName));
            }
            extractKeyFileInfo = new(Path.Combine(fileInfo.DirectoryName, $"{Path.GetFileNameWithoutExtension(fileInfo.Name)}{nugetConfiguration.KeyFileExtension}"));
            if (extractKeyFileInfo.Exists)
            {
                if (extractKeyFileInfo.CreationTime.ToString("yyyy-MM-dd") == fileInfo.CreationTime.ToString("yyyy-MM-dd") && extractKeyFileInfo.LastWriteTime.ToString("yyyy-MM-dd") == fileInfo.LastWriteTime.ToString("yyyy-MM-dd"))
                    continue;
                File.Delete(extractKeyFileInfo.FullName);
            }
            DateTimeOffset? dateTimeOffset = GetDateTimeOffset(nugetConfiguration.KeyFileExtension, fileInfo, extractKeyFileInfo);
            if (dateTimeOffset is null)
                continue;
            SetLastWriteTimes(logger, nugetConfiguration, fileInfo, dateTimeOffset.Value);
        }
    }

    private static int CopyFiles(NugetConfiguration nugetConfiguration, string[] files)
    {
        int results = 0;
        FileInfo fileInfo;
        FileInfo checkFileInfo;
        foreach (string file in files)
        {
            fileInfo = new(file);
            checkFileInfo = new(Path.Combine(nugetConfiguration.Destination, fileInfo.Name));
            if (!checkFileInfo.Exists || fileInfo.LastWriteTime != checkFileInfo.LastWriteTime || fileInfo.Length != checkFileInfo.Length)
            {
                File.Copy(fileInfo.FullName, checkFileInfo.FullName, overwrite: true);
                results++;
            }
            if (string.IsNullOrEmpty(nugetConfiguration.KeyFileExtension))
                continue;
            fileInfo = new(file);
            checkFileInfo = new(Path.Combine(nugetConfiguration.Destination, Path.ChangeExtension(fileInfo.Name, nugetConfiguration.KeyFileExtension)));
            if (!checkFileInfo.Exists || fileInfo.LastWriteTime != checkFileInfo.LastWriteTime || fileInfo.Length != checkFileInfo.Length)
                File.Copy(fileInfo.FullName, checkFileInfo.FullName, overwrite: true);
        }
        return results;
    }

    internal static bool Sync(AppSettings appSettings, ILogger<Worker> logger)
    {
        NugetConfiguration nugetConfiguration = appSettings.NugetConfiguration;
        if (!Directory.Exists(nugetConfiguration.Source))
            _ = Directory.CreateDirectory(nugetConfiguration.Source);
        if (!Directory.Exists(nugetConfiguration.Destination))
            _ = Directory.CreateDirectory(nugetConfiguration.Destination);
        string[] files = Directory.GetFiles(nugetConfiguration.Source, nugetConfiguration.SearchPattern, SearchOption.AllDirectories);
        logger.LogInformation("Found {Files} file(s)", files.Length);
        ExtractKeyFileAndSetDateFromZipEntry(logger, nugetConfiguration, files);
        logger.LogInformation("{Files} file(s) verified", files.Length);
        int filesCopied = CopyFiles(nugetConfiguration, files);
        logger.LogInformation("Copied {FilesCopied} file(s)", filesCopied);
        return true;
    }

}