using System.Runtime.Versioning;
using YMhut.Box.Core.Logging;

namespace YMhut.Box.Core.System;

public sealed record HardwareInfoSummary(
    string OsDescription,
    string CpuName,
    string GpuName,
    string TotalMemory,
    string PrimaryDisk,
    string BoardName,
    string WmiStatus,
    DateTimeOffset CapturedAt);

public interface IHardwareInfoService
{
    Task<HardwareInfoSummary> CaptureAsync(CancellationToken cancellationToken = default);
}

public sealed class HardwareInfoService(ILogService? logService = null) : IHardwareInfoService
{
    public async Task<HardwareInfoSummary> CaptureAsync(CancellationToken cancellationToken = default)
    {
        if (!OperatingSystem.IsWindows())
        {
            return new HardwareInfoSummary(
                global::System.Runtime.InteropServices.RuntimeInformation.OSDescription,
                $"{Environment.ProcessorCount} logical processors",
                "Unavailable",
                "Unavailable",
                "Unavailable",
                "Unavailable",
                "Degraded: WMI is only available on Windows.",
                DateTimeOffset.Now);
        }

        return await CaptureWindowsAsync(cancellationToken).ConfigureAwait(false);
    }

    [SupportedOSPlatform("windows")]
    private async Task<HardwareInfoSummary> CaptureWindowsAsync(CancellationToken cancellationToken)
    {
        return await Task.Run(async () =>
        {
            try
            {
                var summary = new HardwareInfoSummary(
                    global::System.Runtime.InteropServices.RuntimeInformation.OSDescription,
                    FirstWmiValue("Win32_Processor", "Name") ?? $"{Environment.ProcessorCount} logical processors",
                    FirstWmiValue("Win32_VideoController", "Name") ?? "Unknown GPU",
                    FormatMemory(FirstWmiUlong("Win32_ComputerSystem", "TotalPhysicalMemory")),
                    BuildDiskSummary(),
                    BuildBoardSummary(),
                    "OK",
                    DateTimeOffset.Now);
                await WriteLogAsync("Information", "wmi", "Hardware WMI query succeeded", $"cpu={summary.CpuName}; gpu={summary.GpuName}", cancellationToken).ConfigureAwait(false);
                return summary;
            }
            catch (Exception exception)
            {
                await WriteLogAsync("Warning", "wmi", "Hardware WMI query degraded", Sanitize(exception.Message), cancellationToken).ConfigureAwait(false);
                return new HardwareInfoSummary(
                    global::System.Runtime.InteropServices.RuntimeInformation.OSDescription,
                    $"{Environment.ProcessorCount} logical processors",
                    "Unavailable",
                    "Unavailable",
                    "Unavailable",
                    "Unavailable",
                    $"Degraded: {Sanitize(exception.Message)}",
                    DateTimeOffset.Now);
            }
        }, cancellationToken).ConfigureAwait(false);
    }

    [SupportedOSPlatform("windows")]
    private static string? FirstWmiValue(string className, string propertyName)
    {
        using var searcher = new global::System.Management.ManagementObjectSearcher($"SELECT {propertyName} FROM {className}");
        foreach (global::System.Management.ManagementObject item in searcher.Get())
        {
            var value = item[propertyName]?.ToString()?.Trim();
            if (!string.IsNullOrWhiteSpace(value))
            {
                return value;
            }
        }

        return null;
    }

    [SupportedOSPlatform("windows")]
    private static ulong? FirstWmiUlong(string className, string propertyName)
    {
        using var searcher = new global::System.Management.ManagementObjectSearcher($"SELECT {propertyName} FROM {className}");
        foreach (global::System.Management.ManagementObject item in searcher.Get())
        {
            if (ulong.TryParse(item[propertyName]?.ToString(), out var value))
            {
                return value;
            }
        }

        return null;
    }

    [SupportedOSPlatform("windows")]
    private static string BuildDiskSummary()
    {
        using var searcher = new global::System.Management.ManagementObjectSearcher("SELECT Model, Size FROM Win32_DiskDrive");
        var parts = new List<string>();
        foreach (global::System.Management.ManagementObject item in searcher.Get())
        {
            var model = item["Model"]?.ToString()?.Trim();
            var size = ulong.TryParse(item["Size"]?.ToString(), out var bytes) ? FormatMemory(bytes) : "Unknown size";
            if (!string.IsNullOrWhiteSpace(model))
            {
                parts.Add($"{model} ({size})");
            }
        }

        return parts.Count == 0 ? "Unknown disk" : string.Join("; ", parts.Take(3));
    }

    [SupportedOSPlatform("windows")]
    private static string BuildBoardSummary()
    {
        using var searcher = new global::System.Management.ManagementObjectSearcher("SELECT Manufacturer, Product FROM Win32_BaseBoard");
        foreach (global::System.Management.ManagementObject item in searcher.Get())
        {
            var manufacturer = item["Manufacturer"]?.ToString()?.Trim();
            var product = item["Product"]?.ToString()?.Trim();
            var text = string.Join(" ", new[] { manufacturer, product }.Where(value => !string.IsNullOrWhiteSpace(value)));
            if (!string.IsNullOrWhiteSpace(text))
            {
                return text;
            }
        }

        return "Unknown board";
    }

    private static string FormatMemory(ulong? bytes)
    {
        if (bytes is null or 0)
        {
            return "Unknown";
        }

        var value = bytes.Value;
        var units = new[] { "B", "KB", "MB", "GB", "TB" };
        var index = 0;
        var size = (double)value;
        while (size >= 1024 && index < units.Length - 1)
        {
            size /= 1024;
            index++;
        }

        return $"{size:0.##} {units[index]}";
    }

    private Task WriteLogAsync(string level, string category, string message, string? detail, CancellationToken cancellationToken)
    {
        return logService?.WriteAsync(level, category, message, detail, cancellationToken) ?? Task.CompletedTask;
    }

    private static string Sanitize(string message)
    {
        return string.IsNullOrWhiteSpace(message)
            ? string.Empty
            : message.Length > 260 ? message[..260] : message;
    }
}
