using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text.Json;

namespace EpsonPrintService;

public record UsbKeyResult(bool Found, string? Base64Content, string? DecodedApiUrl, string? DecodedRestaurantId, string? Error);

public static class UsbKeyScanner
{
    private const string KeyFileName = "print_server_key.txt";

    public static async Task<UsbKeyResult> ScanForKeyAsync()
    {
        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            return new UsbKeyResult(false, null, null, null, "USB scanning is only supported on Linux.");

        try
        {
            var lsblkJson = await RunCommandAsync("lsblk", "-J -o NAME,RM,MOUNTPOINT,TYPE");
            if (lsblkJson == null)
                return new UsbKeyResult(false, null, null, null, "Failed to run lsblk.");

            var doc = JsonDocument.Parse(lsblkJson);
            var devices = doc.RootElement.GetProperty("blockdevices");

            foreach (var device in devices.EnumerateArray())
            {
                var result = await ScanDeviceAsync(device);
                if (result != null)
                    return result;

                // Check children (partitions)
                if (device.TryGetProperty("children", out var children))
                {
                    foreach (var child in children.EnumerateArray())
                    {
                        result = await ScanDeviceAsync(child);
                        if (result != null)
                            return result;
                    }
                }
            }

            return new UsbKeyResult(false, null, null, null, null);
        }
        catch (Exception ex)
        {
            return new UsbKeyResult(false, null, null, null, $"USB scan failed: {ex.Message}");
        }
    }

    private static async Task<UsbKeyResult?> ScanDeviceAsync(JsonElement device)
    {
        var isRemovable = device.TryGetProperty("rm", out var rm) && (rm.ValueKind == JsonValueKind.True || (rm.ValueKind == JsonValueKind.Number && rm.GetInt32() == 1));
        if (!isRemovable)
            return null;

        var type = device.TryGetProperty("type", out var t) ? t.GetString() : null;
        if (type != "part")
            return null;

        var name = device.TryGetProperty("name", out var n) ? n.GetString() : null;
        if (name == null)
            return null;

        var mountpoint = device.TryGetProperty("mountpoint", out var mp) && mp.ValueKind == JsonValueKind.String ? mp.GetString() : null;

        if (!string.IsNullOrEmpty(mountpoint))
        {
            return CheckMountpoint(mountpoint);
        }

        // Try to mount and check
        var tempMount = $"/tmp/usb_scan_{name}";
        try
        {
            Directory.CreateDirectory(tempMount);
            var mountResult = await RunCommandAsync("mount", $"-o ro /dev/{name} {tempMount}");
            if (mountResult == null)
                return null;

            return CheckMountpoint(tempMount);
        }
        finally
        {
            try { await RunCommandAsync("umount", tempMount); } catch { }
            try { Directory.Delete(tempMount); } catch { }
        }
    }

    private static UsbKeyResult? CheckMountpoint(string mountpoint)
    {
        var keyPath = Path.Combine(mountpoint, KeyFileName);
        if (!File.Exists(keyPath))
            return null;

        var content = File.ReadAllText(keyPath).Trim();
        if (string.IsNullOrWhiteSpace(content))
            return null;

        var validation = ConfigurationLoader.TryLoadFromBase64(content);
        if (!validation.Success)
            return new UsbKeyResult(true, content, null, null, $"Key file found but invalid: {validation.Error}");

        return new UsbKeyResult(true, content, validation.Config!.ApiUrl, validation.Config.RestaurantId, null);
    }

    private static async Task<string?> RunCommandAsync(string command, string arguments)
    {
        try
        {
            using var process = new Process
            {
                StartInfo = new ProcessStartInfo
                {
                    FileName = command,
                    Arguments = arguments,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    UseShellExecute = false,
                    CreateNoWindow = true
                }
            };

            process.Start();
            var output = await process.StandardOutput.ReadToEndAsync();
            await process.WaitForExitAsync();

            return process.ExitCode == 0 ? output : null;
        }
        catch
        {
            return null;
        }
    }
}
