#!/usr/bin/env perl
use strict;
use warnings;

# Helper to read single-line value from a sysfs file
sub read_sysfs {
    my ($path) = @_;
    return unless -f $path && open(my $fh, '<', $path);
    my $val = <$fh>;
    close $fh;
    chomp($val) if defined $val;
    return $val;
}

# 1. Check for NVIDIA GPUs via nvidia-smi (shipped with the driver)
sub query_nvidia {
    my @gpus;
    # Check if nvidia-smi is in PATH
    my $has_nvidia = `which nvidia-smi 2>/dev/null`;
    return @gpus unless $has_nvidia;

    my $cmd = 'nvidia-smi --query-gpu=name,memory.total,memory.used,temperature.gpu,fan.speed --format=csv,noheader,nounits 2>/dev/null';
    open(my $pipe, '-|', $cmd) or return @gpus;

    while (my $line = <$pipe>) {
        chomp $line;
        my ($name, $vram_total, $vram_used, $temp, $fan) = map { s/^\s+|\s+$//gr } split /,/, $line;
        push @gpus, {
            vendor     => 'NVIDIA',
            name       => $name,
            vram_used  => $vram_used eq '[N/A]' ? 'N/A' : "$vram_used MB",
            vram_total => $vram_total eq '[N/A]' ? 'N/A' : "$vram_total MB",
            temp       => $temp eq '[N/A]' ? 'N/A' : "$temp °C",
            fan_speed  => $fan eq '[N/A]' ? 'N/A' : "$fan %",
        };
    }
    close $pipe;
    return @gpus;
}

# 2. Check for AMD / Intel via /sys/class/drm and /sys/class/hwmon
sub query_sysfs_gpus {
    my @gpus;

    for my $card (glob('/sys/class/drm/card[0-9]')) {
        my $device_path = "$card/device";
        next unless -d $device_path;

        # Identify vendor via PCI Vendor ID
        my $vendor_hex = read_sysfs("$device_path/vendor") // '';
        my $vendor = 'Unknown';
        if    ($vendor_hex eq '0x1002') { $vendor = 'AMD'; }
        elsif ($vendor_hex eq '0x8086') { $vendor = 'Intel'; }
        elsif ($vendor_hex eq '0x10de') { $vendor = 'NVIDIA (Nouveau/Open)'; }
        else  { next; } # Skip non-GPU display devices

        # Read VRAM (Standard on AMD/Intel DRM)
        my $vram_used_bytes  = read_sysfs("$device_path/mem_info_vram_used");
        my $vram_total_bytes = read_sysfs("$device_path/mem_info_vram_total");

        my $vram_used  = defined $vram_used_bytes  ? sprintf('%.0f MB', $vram_used_bytes / (1024 * 1024)) : 'N/A';
        my $vram_total = defined $vram_total_bytes ? sprintf('%.0f MB', $vram_total_bytes / (1024 * 1024)) : 'N/A';

        # Search attached hwmon directories for sensor readings
        my ($temp, $fan) = ('N/A', 'N/A');
        my ($hwmon_dir) = glob("$device_path/hwmon/hwmon*");
        if ($hwmon_dir && -d $hwmon_dir) {
            # Temperature: sysfs reports in millidegrees C
            my $temp_raw = read_sysfs("$hwmon_dir/temp1_input");
            $temp = sprintf('%.0f °C', $temp_raw / 1000) if defined $temp_raw;

            # Fan: may expose RPM (fan1_input) or PWM percentage (pwm1)
            my $fan_rpm = read_sysfs("$hwmon_dir/fan1_input");
            my $fan_pwm = read_sysfs("$hwmon_dir/pwm1");
            if (defined $fan_rpm) {
                $fan = "$fan_rpm RPM";
            } elsif (defined $fan_pwm) {
                $fan = sprintf('%.0f %%', ($fan_pwm / 255) * 100);
            }
        }

        push @gpus, {
            vendor     => $vendor,
            name       => basename_card($card),
            vram_used  => $vram_used,
            vram_total => $vram_total,
            temp       => $temp,
            fan_speed  => $fan,
        };
    }
    return @gpus;
}

sub basename_card { (split '/', shift)[-1] }

# Collect and output
my @all_gpus = (query_nvidia(), query_sysfs_gpus());

for my $gpu (@all_gpus) {
    print "[$gpu->{vendor}] $gpu->{name}\n";
    print "  VRAM: $gpu->{vram_used} / $gpu->{vram_total}\n";
    print "  Temp: $gpu->{temp}\n";
    print "  Fan:  $gpu->{fan_speed}\n\n";
}