using System.Diagnostics;

namespace Lumi.Companion.App;

internal static class UpdateApplier
{
    public static bool IsApplyRequest(string[] args) => args.Length > 0 && args[0].Equals("--apply-update", StringComparison.OrdinalIgnoreCase);

    public static int Apply(string[] args)
    {
        if (args.Length != 7 || !int.TryParse(args[1], out var processId)) return 3;
        var stageRoot = Path.GetFullPath(args[2]);
        var staged = Path.GetFullPath(args[3]);
        var target = Path.GetFullPath(args[4]);
        var expectedHash = args[5];
        var helper = Path.GetFullPath(args[6]);
        try
        {
            try { Process.GetProcessById(processId).WaitForExit(30000); } catch (ArgumentException) { }
            if (!File.Exists(staged) || !staged.StartsWith(stageRoot + Path.DirectorySeparatorChar, StringComparison.OrdinalIgnoreCase) ||
                !UpdateService.HashFile(staged).Equals(expectedHash, StringComparison.OrdinalIgnoreCase)) return 4;
            var targetRoot = Path.GetDirectoryName(target)!;
            var stagedComponents = Path.Combine(stageRoot, "components");
            if (Directory.Exists(stagedComponents)) ReplaceDirectory(stagedComponents, Path.Combine(targetRoot, "components"));
            var temporary = $"{target}.{Environment.ProcessId}.update";
            var backup = $"{target}.previous";
            File.Copy(staged, temporary, true);
            if (File.Exists(target)) File.Copy(target, backup, true);
            File.Move(temporary, target, true);
            Process.Start(new ProcessStartInfo(target) { UseShellExecute = true });
            try { Directory.Delete(stageRoot, true); } catch { }
            try { File.Delete(helper); } catch { }
            return 0;
        }
        catch { return 5; }
    }

    private static void ReplaceDirectory(string source, string target)
    {
        var temporary = $"{target}.{Environment.ProcessId}.update";
        var backup = $"{target}.previous";
        if (Directory.Exists(temporary)) Directory.Delete(temporary, true);
        CopyDirectory(source, temporary);
        if (Directory.Exists(backup)) Directory.Delete(backup, true);
        if (Directory.Exists(target)) Directory.Move(target, backup);
        Directory.Move(temporary, target);
    }

    private static void CopyDirectory(string source, string target)
    {
        Directory.CreateDirectory(target);
        foreach (var directory in Directory.EnumerateDirectories(source, "*", SearchOption.AllDirectories))
            Directory.CreateDirectory(Path.Combine(target, Path.GetRelativePath(source, directory)));
        foreach (var file in Directory.EnumerateFiles(source, "*", SearchOption.AllDirectories))
            File.Copy(file, Path.Combine(target, Path.GetRelativePath(source, file)), true);
    }
}
