using Katteker.Utilities;
using KattekerCreator.Exceptions;
using KattekerCreator.Types;
using Serilog;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading;

namespace KattekerCreator.Helper
{
    public static class Utils
    {
        public static bool ExecuteProcess(string executable, string arguments, bool verbose)
        {
            Log.Information($"Execute {Path.GetFileName(executable)}");
            using var p = new Process
            {
                StartInfo = new ProcessStartInfo
                {
                    FileName = executable,
                    Arguments = arguments,
                    WindowStyle = ProcessWindowStyle.Hidden,
                    UseShellExecute = !verbose,
                }
            };
            p.Start();
            p.WaitForExit(180000);
            var exitCode = p.ExitCode;
            var elapsedSeconds = (p.ExitTime - p.StartTime).TotalSeconds;
            Log.Information($"{Path.GetFileName(executable)} has exited with exit code: {exitCode} (Took: {elapsedSeconds:F3}s)");
            return exitCode == 0;
        }

        public static IEnumerable<string> FileExclusions(IEnumerable<string>? userDefinedFileExclusions)
        {
            yield return "*.pdb";
            yield return "*.tmp";
            yield return "*.obj";
            yield return "*.pch";
            yield return "*.vshost.exe*";
            yield return "squirrelHelperInfo.json";
            yield return "Katteker.config";

            if (userDefinedFileExclusions is null) yield break;
            foreach (var fileExclusion in userDefinedFileExclusions)
            {
                yield return fileExclusion;
            }
        }

        public static string BaseDirectory { get; } = AppContext.BaseDirectory ??
                   throw new KattekerCreatorException("Base Directory is null");

        public static string GetSignTool() => Path.Combine(BaseDirectory, "contrib", "signtool.exe");

        /// <summary>
        /// Get the signtool.exe command line. Returns "" if no certificate string is provided.
        /// </summary>
        /// <param name="certificate"></param>
        /// <param name="password"></param>
        /// <param name="filename"></param>
        /// <returns></returns>
        public static string GetSignToolCommandLine(string certificate, string password, string filename)
        {
            return string.IsNullOrWhiteSpace(certificate)
                ? string.Empty
                : $"\"{GetSignTool()}\" {GetSignToolParameters(certificate, password, filename)}";
        }

        public static string GetSignToolParameters(string certificate, string password, string filename)
        {
            return $"sign /fd SHA256 /t http://timestamp.digicert.com /f \"{certificate}\" /p {password} {filename}";
        }

        public static void SignExecutable(string certificate, string password, string filename, bool verbose)
        {
            if (string.IsNullOrWhiteSpace(certificate)) return;

            var successExecuteProcess = ExecuteProcess(GetSignTool(),
                GetSignToolParameters(certificate, password, filename),
                verbose);
            if (!successExecuteProcess) throw new KattekerCreatorException("AppStub could not signed.");
        }

        public static bool TryDeleteDirectory(DirectoryInfo tempDir, int millisecondsDelay)
        {
            if (Debugger.IsAttached) return true;
            try
            {
                Thread.Sleep(millisecondsDelay);
                tempDir.Delete(true);
                return true;
            }
            catch
            {
                return false;
            }
        }

        public static string GenerateFilename(string name, string version, string extension, string? channel,
            bool isDelta = false) =>
            $"{name}{channel}-{version}-{(isDelta ? "delta" : "full")}.{extension}";

        public static void WriteHeader()
        {
            Console.WriteLine(@"             |\=.     ");
            Console.WriteLine(@"             /  6',	  ");
            Console.WriteLine(@"     .--.    \  .-'	  ");
            Console.WriteLine(@"    /_   \   /  (_()  ");
            Console.WriteLine(@"      )   | / `;--'	  ");
            Console.WriteLine(@"     /   / /   (	  ");
            Console.WriteLine("    (    `\"    _)_	  ");
            Console.WriteLine(@"jgs  `-==-'`""""""`	  ");
            Console.WriteLine();
            var version = VersionUtils.GetFullVersion;
            Console.WriteLine($"katteker-cli v{version}");
            Console.WriteLine("-------------");
        }

        internal static void WriteAssemblyInfoToLog(AssemblyFileInfo assemblyFileInfo)
        {
            Log.Information($"  File name: {assemblyFileInfo.FileName}");
            Log.Information($"  Version: {assemblyFileInfo.AssemblyVersion}");
            Log.Information($"  Company: {assemblyFileInfo.Company}");
            Log.Information($"  Copyright: {assemblyFileInfo.Copyright}");
            Log.Information($"  Product name: {assemblyFileInfo.ProductName}");
        }

        internal static void WriteOutputSizeToLog(string assemblyFileInfo, string archive)
        {
            const double divisor = 1048576D;
            var sizeInMegaByte = new DirectoryInfo(assemblyFileInfo)
                .EnumerateFiles()
                .Sum(x => x.Length) / divisor;
            var archiveSize = new FileInfo(archive).Length / divisor;
            Log.Information($"  Folder size: {sizeInMegaByte:F2} MB");
            Log.Information($"  Compressed size: {archiveSize:F2} MB");
            Log.Information($"  Compression ratio: {archiveSize / sizeInMegaByte:P2}");
        }

        public static string GetContentOfFile(string? path)
        {
            if (string.IsNullOrWhiteSpace(path)) return "";
            return File.Exists(path)
                ? File.ReadAllText(path)
                : throw new KattekerCreatorException($"{path} not found.");
        }
    }
}