using System.Diagnostics;
using System.Text.Json;
using System.Text.RegularExpressions;

namespace CodeRemoteManager.SetupApp;

public sealed record DevicePairingResult(string Code, DateTimeOffset ExpiresAt, string QrPayload);

public sealed class DevicePairingException() : Exception("pairing_creation_failed");

public sealed partial class DevicePairingLauncher(string nodePath, string cliPath, string configurationPath)
{
    public async Task<DevicePairingResult> CreateAsync(string origin)
    {
        try
        {
            var start = new ProcessStartInfo(nodePath)
            {
                RedirectStandardInput = true,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = true
            };
            start.ArgumentList.Add(cliPath);
            start.Environment["CODE_REMOTE_MANAGER_CONFIG_PATH"] = configurationPath;
            using var process = Process.Start(start) ?? throw new DevicePairingException();
            var outputTask = process.StandardOutput.ReadToEndAsync();
            var errorTask = process.StandardError.ReadToEndAsync();
            await process.StandardInput.WriteAsync(JsonSerializer.Serialize(new { origin }));
            process.StandardInput.Close();
            using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(10));
            try
            {
                await process.WaitForExitAsync(timeout.Token);
            }
            catch (OperationCanceledException)
            {
                if (!process.HasExited) process.Kill(entireProcessTree: true);
                throw new DevicePairingException();
            }
            var output = await outputTask;
            _ = await errorTask;
            if (process.ExitCode != 0) throw new DevicePairingException();
            return ParseResult(output);
        }
        catch (DevicePairingException)
        {
            throw;
        }
        catch
        {
            throw new DevicePairingException();
        }
    }

    public static DevicePairingResult ParseResult(string output)
    {
        try
        {
            using var document = JsonDocument.Parse(output);
            var root = document.RootElement;
            var code = root.GetProperty("code").GetString() ?? "";
            var expiresAtText = root.GetProperty("expiresAt").GetString() ?? "";
            var qrPayload = root.GetProperty("qrPayload").GetString() ?? "";
            if (!PairingCodePattern().IsMatch(code) ||
                !DateTimeOffset.TryParse(expiresAtText, out var expiresAt) ||
                !qrPayload.StartsWith("crm-pair:v1:", StringComparison.Ordinal) ||
                qrPayload.Length > 2048)
                throw new DevicePairingException();
            return new DevicePairingResult(code, expiresAt, qrPayload);
        }
        catch (DevicePairingException)
        {
            throw;
        }
        catch
        {
            throw new DevicePairingException();
        }
    }

    [GeneratedRegex("^[0-9A-HJKMNP-TV-Z]{4}-[0-9A-HJKMNP-TV-Z]{4}-[0-9A-HJKMNP-TV-Z]{4}$", RegexOptions.CultureInvariant)]
    private static partial Regex PairingCodePattern();
}
