#!/usr/bin/env perl
#===============================================================================
# sysinfo.pl - System Information Collector for Sysops
#
# A read-only, non-destructive system information gathering tool.
# Detects macOS (Darwin) or Linux and uses appropriate commands.
#===============================================================================

use strict;
use warnings;
use utf8;
use open qw(:std :utf8);

# --- Configuration ---
my $MAX_PROCESSES = 15;      # Number of top processes to show
my $DISK_THRESHOLD_WARN = 80; # Percentage for disk warning
my $MEM_THRESHOLD_WARN  = 80; # Percentage for memory warning

# --- OS Detection ---
my $OS = '';
if (-e '/bin/darwin_version' || `uname` =~ /Darwin/i) {
    $OS = 'darwin';
} elsif (-e '/bin/linux' || `uname` =~ /Linux/i) {
    $OS = 'linux';
} else {
    die "Unsupported operating system.\n";
}

print_header();

# --- System Overview ---
print_section("SYSTEM OVERVIEW");
print_os_info();
print_uptime();
print_hostname();

# --- Hardware ---
print_section("HARDWARE");
print_cpu_info();
print_memory_info();

# --- Disks ---
print_section("DISK USAGE");
print_disk_info();

# --- Network ---
print_section("NETWORK INTERFACES");
print_network_info();

# --- Processes ---
print_section("TOP PROCESSES (by CPU)");
print_top_processes('cpu');

print_section("TOP PROCESSES (by Memory)");
print_top_processes('mem');

# --- Users ---
print_section("LOGGED IN USERS");
print_users();

print_footer();

exit 0;

#===============================================================================
# SUBROUTINES
#===============================================================================

sub print_header {
    my $sep = "=" x 70;
    my $date = `date '+%Y-%m-%d %H:%M:%S %Z'`;
    chomp $date;
    print "$sep\n";
    print "  SYSTEM INFORMATION REPORT\n";
    print "  Generated: $date\n";
    print "  OS: $OS\n";
    print "$sep\n\n";
}

sub print_section {
    my ($title) = @_;
    print "\n" . "-" x 70 . "\n";
    print "  $title\n";
    print "-" x 70 . "\n";
}

sub print_footer {
    print "\n" . "=" x 70 . "\n";
    print "  End of Report\n";
    print "=" x 70 . "\n";
}

sub print_os_info {
    if ($OS eq 'darwin') {
        my $product = `sw_vers -productVersion`;
        my $build   = `sw_vers -buildVersion`;
        my $name    = `sw_vers -productName`;
        chomp $product;
        chomp $build;
        chomp $name;
        printf "  Product:    %s\n", $name // 'Unknown';
        printf "  Version:    %s\n", $product // 'Unknown';
        printf "  Build:      %s\n", $build   // 'Unknown';
    } else {
        my $distro = `cat /etc/os-release 2>/dev/null | grep '^PRETTY_NAME=' | cut -d'\"' -f2`;
        chomp $distro;
        my $kernel = `uname -r`;
        chomp $kernel;
        printf "  Distro:     %s\n", $distro // 'Unknown';
        printf "  Kernel:     %s\n", $kernel  // 'Unknown';
    }
}

sub print_uptime {
    if ($OS eq 'darwin') {
        my $up = `uptime`;
        chomp $up;
        print "  Uptime:     $up\n";
    } else {
        my $up = `uptime -p 2>/dev/null`;
        chomp $up;
        if ($up =~ /up/i) {
            print "  Uptime:     $up\n";
        } else {
            my $raw = `uptime`;
            chomp $raw;
            print "  Uptime:     $raw\n";
        }
    }
}

sub print_hostname {
    my $host = `hostname`;
    chomp $host;
    print "  Hostname:   $host\n";
}

sub print_cpu_info {
    if ($OS eq 'darwin') {
        my $cpu_name = `sysctl -n machdep.cpu.brand_string`;
        my $cores    = `sysctl -n hw.physicalcpu`;
        my $threads  = `sysctl -n hw.logicalcpu`;
        my $speed    = `sysctl -n hw.cpufrequency 2>/dev/null`;
        chomp $cpu_name;
        chomp $cores;
        chomp $threads;

        my $speed_ghz = '';
        if ($speed =~ /^(\d+)$/) {
            $speed_ghz = sprintf("%.2f GHz", $1 / 1e9);
        }
        
        printf "  CPU:        %s\n", $cpu_name // 'Unknown';
        print "  Cores:      $cores\n" if $cores;
        print "  Threads:    $threads\n" if $threads;
        print "  Speed:      $speed_ghz\n" if $speed_ghz;
    } else {
        my $cpu_info = `lscpu 2>/dev/null | grep -E '^(Model name|CPU\(s\)|Thread|Core|Socket|CPU MHz|CPU max MHz)'`;
        for my $line (split /\n/, $cpu_info) {
            next unless $line =~ /\S/;
            print "  $line\n";
        }
    }
}

sub print_memory_info {
    if ($OS eq 'darwin') {
        my $phys_mem = `sysctl -n hw.memsize`;
        chomp $phys_mem;
        if ($phys_mem =~ /^(\d+)$/) {
            my $gb = sprintf("%.2f", $1 / (1024**3));
            print "  Total:      ${gb} GB\n";
        }

        # Get memory pressure/stats
        my $mem_stat = `vm_stat`;
        if ($mem_stat) {
            # Page size is typically 4096 bytes on macOS
            my %pages;
            while ($mem_stat =~ /(\w+)\s*:\s*(\d+)\s*pages/g) {
                $pages{$1} = $2;
            }
            my $page_size = 4096;
            my $free      = ($pages{'Pages free'} // 0) * $page_size;
            my $active    = ($pages{'Pages active'} // 0) * $page_size;
            my $inactive  = ($pages{'Pages inactive'} // 0) * $page_size;
            my $wired     = ($pages{'Pages wired down'} // 0) * $page_size;
            
            my $used = $active + $wired;
            my $total = $phys_mem // 0;
            my $percent = ($total > 0) ? sprintf("%.1f", ($used / $total) * 100) : "N/A";
            
            printf "  Free:       %.2f GB\n", $free / (1024**3) if $free;
            printf "  Active:     %.2f GB\n", $active / (1024**3) if $active;
            printf "  Inactive:   %.2f GB\n", $inactive / (1024**3) if $inactive;
            printf "  Wired:      %.2f GB\n", $wired / (1024**3) if $wired;
            printf "  Used:       %.1f%%\n", $percent;
        }

    } else {
        # Linux - use free command
        my $free_output = `free -h 2>/dev/null`;
        if ($free_output) {
            print "\n";
            while (my $line = <$free_output>) {
                chomp $line;
                next unless $line =~ /\S/;
                print "  $line\n";
            }
        }

        # Also show /proc/meminfo summary
        my $meminfo = `cat /proc/meminfo 2>/dev/null | head -10`;
        while ($meminfo =~ /([^\n]+)\n?/g) {
            my $line = $1;
            last unless $line =~ /\S/;
            print "  $line\n";
        }
    }
}

sub print_disk_info {
    if ($OS eq 'darwin') {
        my $disk_output = `df -h 2>/dev/null`;
        if ($disk_output) {
            print "\n";
            for my $line (split /\n/, $disk_output) {
                next unless $line =~ /\S/;

                # Skip header line
                next if $line =~ /^Filesystem/;

                # Check for threshold warning
                if ($line =~ /(\d+)%\s*$/ && $1 >= $DISK_THRESHOLD_WARN) {
                    print "  ⚠  $line\n";
                } else {
                    print "  $line\n";
                }
            }
        }
    } else {
        my $disk_output = `df -h 2>/dev/null`;
        if ($disk_output) {
            for my $line (split /\n/, $disk_output) {
                next unless $line =~ /\S/;

                # Check for threshold warning
                if ($line =~ /(\d+)%\s*$/ && $1 >= $DISK_THRESHOLD_WARN) {
                    print "  ⚠  $line\n";
                } else {
                    print "  $line\n";
                }
            }
        }
    }
}

sub print_network_info {
    if ($OS eq 'darwin') {
        # List network interfaces
        my $ifconfig = `ifconfig 2>/dev/null`;
        if ($ifconfig) {
            my $current_iface = '';
            for my $line (split /\n/, $ifconfig) {
                if ($line =~ /^(\w+)/ && $1 ne 'utun') {
                    $current_iface = $1;
                }
                if ($current_iface && $line =~ /\tinet (\d+\.\d+\.\d+\.\d+)/) {
                    printf "  %-15s IP: %s\n", $current_iface, $1;
                }
            }

            # Also show default route
            my $route = `netstat -nr 2>/dev/null | grep '^default.*en' | head -1`;
            chomp $route;
            print "  Default GW: $route\n" if $route;
        }
    } else {
        # Linux - use ip command or ifconfig
        my $ip_output = `ip addr 2>/dev/null`;
        if ($ip_output) {
            for my $line (split /\n/, $ip_output) {
                if ($line =~ /(\d+):\s+(\w+)/) {
                    print "  Interface: $2\n";
                }
                if ($line =~ /inet (\d+\.\d+\.\d+\.\d+)\/(\d+)/) {
                    printf "    IP: %s/%s\n", $1, $2;
                }
            }

            # Default route
            my $route = `ip route 2>/dev/null | grep default`;
            chomp $route;
            print "  Default:    $route" if $route;
        } else {
            # Fallback to ifconfig
            my $ifconfig = `ifconfig 2>/dev/null`;
            if ($ifconfig) {
                for my $line (split /\n/, $ifconfig) {
                    next unless $line =~ /\S/;
                    print "  $line\n" if $line =~ /(inet |inet6 |ether |txqueuelen)/;
                }
            }
        }
    }
}

sub print_top_processes {
    my ($sort_by) = @_;  # 'cpu' or 'mem'

    if ($OS eq 'darwin') {
        my $proc_output = `ps aux --sort=-%cpu 2>/dev/null | head -${MAX_PROCESSES}1`;
        if ($proc_output) {
            print "\n";
            for my $line (split /\n/, $proc_output) {
                print "  $line\n";
            }
        }
    } else {
        my $sort_flag = ($sort_by eq 'cpu') ? '--sort=-%cpu' : '--sort=-%mem';
        my $proc_output = `top -b -n 1 -o $sort_by 2>/dev/null | head -${MAX_PROCESSES}20`;
        
        if ($proc_output !~ /\S/) {
            # Fallback to ps for htop/notop systems
            $proc_output = `ps aux --sort=$sort_flag 2>/dev/null | head -${MAX_PROCESSES}1`;
        }
        
        if ($proc_output) {
            for my $line (split /\n/, $proc_output) {
                next unless $line =~ /\S/;
                print "  $line\n";
            }
        }
    }
}

sub print_users {
    if ($OS eq 'darwin') {
        my $who = `who 2>/dev/null`;
        if ($who) {
            print "\n";
            for my $line (split /\n/, $who) {
                print "  $line\n";
            }
        } else {
            print "  No users logged in.\n";
        }
    } else {
        my $who = `who 2>/dev/null`;
        if ($who) {
            for my $line (split /\n/, $who) {
                print "  $line\n";
            }
        } else {
            my $last = `who /var/run/utmp 2>/dev/null || w 2>/dev/null`;
            if ($last) {
                for my $line (split /\n/, $last) {
                    print "  $line\n";
                }
            } else {
                print "  No users logged in.\n";
            }
        }
    }
}
