#!/usr/bin/env perl

use lib 'lib';
use History::SQLite;
use Environment::SQLite;
use Term::ReadLine;
use Cwd;
use File::HomeDir;
use CustomCommands;
use POSIX qw(getlogin getuid setsid tcsetpgrp);

# there is only one

setsid(); # Start a new session
tcsetpgrp(0, getpgrp()); # Set TTY foreground process group

# some env vars

$ENV{HOME}  ||= File::HomeDir->my_home;
$ENV{USER}  ||= getlogin() || (getpwuid(getuid()))[0] || 'user';
$ENV{LOGNAME} ||= $ENV{USER};
$ENV{SHELL} ||= $0; # Path to pshell.pl or compiled binary
$ENV{PATH}  ||= '/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin';
$ENV{TERM}  ||= 'xterm-256color';

# we a login shell?

my $is_login_shell = ($0 =~ /^-/ || $ARGV[0] eq '-l');
if ($is_login_shell) {
    # Clear inherited environment for security, except essentials
    %ENV = (
        HOME => $ENV{HOME},
        USER => $ENV{USER},
        LOGNAME => $ENV{LOGNAME},
        SHELL => $ENV{SHELL},
        TERM => $ENV{TERM},
    );
    # Reload system and user configs
}

# system wide env vars

# Parse /etc/environment silently
if (-r '/etc/environment') {
    open my $fh, '<', '/etc/environment' or do {
        warn "Could not open /etc/environment: $!" if $is_login_shell;
        next;
    };
    while (my $line = <$fh>) {
        chomp $line;
        next unless $line =~ /^(\w+)=(.*)$/;
        $ENV{$1} = $2 unless exists $ENV{$1};
    }
    close $fh;
}

# Source /etc/profile silently
if (-r '/etc/profile') {
    # Run /etc/profile in a subshell, capture env output, and suppress other prints
    my $env_output = qx(/bin/sh -c '. /etc/profile >/dev/null 2>&1 && env');
    if ($? == 0) {
        while ($env_output =~ /^(\w+)=(.*)$/mg) {
            $ENV{$1} = $2 unless exists $ENV{$1};
        }
    } else {
        warn "Could not source /etc/profile: $!" if $is_login_shell;
    }
}


# Initialize command history and environment
my $history = History::SQLite->new(
    db_path => 'pshell_history.db'
);
my $env = Environment::SQLite->new(
    db_path => 'pshell_env.db'
);

# Signal handling setup
$SIG{INT} = sub { print "\n"; };
$SIG{CHLD} = 'IGNORE';

print "Perl Shell (pshell) - Type 'exit' to quit\n";

my $term = Term::ReadLine->new('pshell');
$term->ornaments(0);

# Workaround for Ctrl-R binding with Term::ReadLine::Gnu
$term->add_defun('history-search', sub {
    my ($count, $key) = @_;
    my $search_term = $term->readline('Search history: ');
    return unless defined $search_term && length $search_term;
    
    my @matches = grep { $_ =~ /\Q$search_term\E/i } $history->get_all;
    if (@matches) {
        print "\nMatching commands:\n";
        for my $i (0..$#matches) {
            print sprintf("%2d: %s\n", $i+1, $matches[$i]);
        }
        
        my $choice = $term->readline('\nSelect command to run (number or Enter to cancel): ');
        if ($choice =~ /^\d+$/ && $choice > 0 && $choice <= @matches) {
            $term->addhistory($matches[$choice-1]);
            $term->replace_line($matches[$choice-1]);
            $term->redisplay;
        }
    } else {
        print "\nNo matching commands found\n";
    }
    
    return 0;
});
$term->parse_and_bind('"\C-r": history-search');

# Load previous history
my @history = $history->get_all;
$term->addhistory($_) for @history;

# Load environment variables
my %env_vars = $env->get_all;
$ENV{$_} = $env_vars{$_} for keys %env_vars;

# Load and execute .pshellrc if it exists
my $rc_file = File::HomeDir->my_home . '/.pshellrc';
if (-e $rc_file && -r $rc_file) {
    open my $fh, '<', $rc_file or warn "Could not open $rc_file: $!";
    while (my $line = <$fh>) {
        chomp $line;
        next unless length $line;
        if ($line =~ /^(\w+)=(.*)$/) {
            $ENV{$1} = $2;
            $env->set($1, $2);
        } else {
            system($line);
        }
    }
    close $fh;
}

while (1) {
    my $cwd = getcwd();
    my $prompt = "pshell:$cwd> ";
    my $command = $term->readline($prompt);
    
    last unless defined $command;
    chomp $command;
    last if $command =~ /^exit$/i;
    
    # Skip empty commands
    next unless length $command;
    
    # Handle command
    my $cmd_status = CustomCommands::handle($command, $env);
    if ($cmd_status >= 0) {  # 1=handled, 2=system command
        # Save valid commands to history
        $history->add($command);
        $term->addhistory($command);
        next;
    }
    
    # Command not found
    print "Command not found: $command\n";
    next;
}
