#!/usr/bin/env perl

use strict;
use warnings;
use JSON::PP;
use IO::File;
use FindBin;
use Term::ANSIColor;
use lib "$FindBin::Bin/../lib";
use JQ::Lite;

my $decoder;
my $decoder_module;
my $decoder_debug = 0;
my $decoder_choice;

# Parse command-line args before anything else
for (my $i = 0; $i < @ARGV; $i++) {
    if ($ARGV[$i] eq '--debug') {
        $decoder_debug = 1;
        splice @ARGV, $i, 1;
        redo;
    }
    if ($ARGV[$i] eq '--use') {
        $decoder_choice = $ARGV[$i + 1];
        splice @ARGV, $i, 2;
        redo;
    }
}

if ($decoder_choice) {
    if ($decoder_choice eq 'JSON::MaybeXS') {
        require JSON::MaybeXS;
        $decoder = \&JSON::MaybeXS::decode_json;
        $decoder_module = 'JSON::MaybeXS';
    }
    elsif ($decoder_choice eq 'Cpanel::JSON::XS') {
        require Cpanel::JSON::XS;
        $decoder = \&Cpanel::JSON::XS::decode_json;
        $decoder_module = 'Cpanel::JSON::XS';
    }
    elsif ($decoder_choice eq 'JSON::XS') {
        require JSON::XS;
        $decoder = \&JSON::XS::decode_json;
        $decoder_module = 'JSON::XS';
    }
    elsif ($decoder_choice eq 'JSON::PP') {
        require JSON::PP;
        $decoder = \&JSON::PP::decode_json;
        $decoder_module = 'JSON::PP';
    }
    else {
        die "[ERROR] Unknown JSON module: $decoder_choice\n";
    }
}
else {
    if (eval { require JSON::MaybeXS; 1 }) {
        $decoder = \&JSON::MaybeXS::decode_json;
        $decoder_module = 'JSON::MaybeXS';
    }
    elsif (eval { require Cpanel::JSON::XS; 1 }) {
        $decoder = \&Cpanel::JSON::XS::decode_json;
        $decoder_module = 'Cpanel::JSON::XS';
    }
    elsif (eval { require JSON::XS; 1 }) {
        $decoder = \&JSON::XS::decode_json;
        $decoder_module = 'JSON::XS';
    }
    else {
        require JSON::PP;
        $decoder = \&JSON::PP::decode_json;
        $decoder_module = 'JSON::PP';
    }
}

# debug
warn "[DEBUG] Using $decoder_module\n" if $decoder_debug;

# Show help if no arguments are given
if (!@ARGV) {
    print <<'USAGE';
jq-lite - A lightweight jq-like JSON query tool written in pure Perl

Usage:
  jq-lite [options] '.query' [file.json]

Options:
  -r, --raw-output     Print raw strings instead of JSON-encoded values
  --color              Colorize JSON output (keys, strings, numbers, booleans)
  --use <Module>       Force JSON decoder module (e.g. JSON::PP, JSON::XS, Cpanel::JSON::XS)
  --debug              Show which JSON module is being used
  -h, --help           Show this help message
  -v, --version        Show version information

Examples:
  cat users.json | jq-lite '.users[].name'
  jq-lite '.users[] | select(.age > 25)' users.json
  jq-lite -r '.users[] | .name' users.json
  jq-lite '.meta has "version"' config.json
  jq-lite --color '.items | sort | reverse | first' data.json

Homepage:
  https://metacpan.org/pod/JQ::Lite
USAGE
    exit 0;
}

# Argument parsing
my $raw_output   = 0;
my $color_output = 0;
my $query;
my $filename;

while (@ARGV) {
    my $arg = shift @ARGV;
    if ($arg eq '--raw-output' || $arg eq '-r') {
        $raw_output = 1;
    }
    elsif ($arg eq '--color') {
        $color_output = 1;
    }
    elsif ($arg eq '--help' || $arg eq '-h') {
        print <<'USAGE';
jq-lite - A lightweight jq-like JSON query tool written in pure Perl

Usage:
  jq-lite [options] '.query' [file.json]

Options:
  -r, --raw-output     Print raw strings instead of JSON-encoded values
  --color              Colorize JSON output (keys, strings, numbers, booleans)
  --use <Module>       Force JSON decoder module (e.g. JSON::PP, JSON::XS, Cpanel::JSON::XS)
  --debug              Show which JSON module is being used
  -h, --help           Show this help message
  -v, --version        Show version information

Examples:
  cat users.json | jq-lite '.users[].name'
  jq-lite '.users[] | select(.age > 25)' users.json
  jq-lite -r '.users[] | .name' users.json
  jq-lite '.meta has "version"' config.json
  jq-lite --color '.items | sort | reverse | first' data.json

Homepage:
  https://metacpan.org/pod/JQ::Lite
USAGE
        exit 0;
    }
    elsif ($arg eq '--version' || $arg eq '-v') {
        print "jq-lite version $JQ::Lite::VERSION\n";
        exit 0;
    }
    elsif (!defined $query && $arg =~ /^\./) {
        $query = $arg;
    }
    elsif (!defined $query && !-f $arg) {
        die "Error: Invalid query syntax '$arg'\nUsage: jq-lite [options] '.query' [file.json]\n";
    }
    elsif (!defined $query) {
        $filename = $arg;
    }
    elsif (!defined $filename) {
        $filename = $arg;
    }
    else {
        die "Usage: jq-lite [options] '.query' [file.json]\n";
    }
}

# Load JSON from file or STDIN
my $json_text;
if (defined $filename) {
    open my $fh, '<', $filename or die "Cannot open file '$filename': $!\n";
    local $/;
    $json_text = <$fh>;
    close $fh;
}
else {
    local $/;
    $json_text = <STDIN>;
}

# Create JQ::Lite object
my $jq = JQ::Lite->new(raw => $raw_output);

# Colorizing helper
sub colorize_json {
    my $json = shift;

    $json =~ s/"([^"]+)"(?=\s*:)/color("cyan")."\"$1\"".color("reset")/ge;
    $json =~ s/: "([^"]*)"/": ".color("green")."\"$1\"".color("reset")/ge;
    $json =~ s/: (\d+(\.\d+)?)/": ".color("yellow")."$1".color("reset")/ge;
    $json =~ s/: (true|false|null)/": ".color("magenta")."$1".color("reset")/ge;

    return $json;
}

# Output helper
sub print_results {
    my @results = @_;
    my $pp = JSON::PP->new->utf8->canonical->pretty;

    for my $r (@results) {
        if (!defined $r) {
            print "null\n";
        }
        elsif ($raw_output && !ref($r)) {
            print "$r\n";
        }
        else {
            my $json = $pp->encode($r);
            $json = colorize_json($json) if $color_output;
            print $json;
        }
    }
}

# Interactive mode
if (!defined $query) {
    system("stty -icanon -echo");

    $SIG{INT} = sub {
        system("stty sane");
        print "\n[EXIT]\n";
        exit 0;
    };

    my $input = '';
    my @last_results;

    my $ok = eval {
        @last_results = $jq->run_query($json_text, '.');
        1;
    };
    if (!$ok || !@last_results) {
        my $data = eval { JSON::PP->new->decode($json_text) };
        if ($data) {
            @last_results = ($data);
        }
    }

    system("clear");
    print "> $input\n";
    if (@last_results) {
        print_results(@last_results);
    } else {
        print "[INFO] Failed to load initial JSON data.\n";
    }

    print "\nType query (ESC to quit):\n";

    while (1) {
        my $char;
        sysread(STDIN, $char, 1);

        my $ord = ord($char);
        last if $ord == 27;

        if ($ord == 127 || $char eq "\b") {
            chop $input if length($input);
        } else {
            $input .= $char;
        }

        system("clear");
        print "> $input\n";

        my @results;
        my $ok = eval {
            @results = $jq->run_query($json_text, $input);
            1;
        };

        if ($ok && @results) {
            @last_results = @results;
        }

        if (!$ok) {
            print "[INFO] Invalid or partial query. Showing last valid results.\n";
        } elsif (!@results) {
            print "[INFO] Query returned no results. Showing last valid results.\n";
        }

        if (@last_results) {
            eval {
                print_results(@last_results);
                1;
            } or do {
                my $e = $@ || 'Unknown error';
                print "[ERROR] Failed to print: $e\n";
            };
        } else {
            print "[INFO] No previous valid results.\n";
        }
    }

    system("stty sane");
    print "\nGoodbye.\n";
    exit 0;
}

# One-shot mode
my @results = eval { $jq->run_query($json_text, $query) };
if ($@) {
    die "[ERROR] Invalid query: $@\n";
}

if (!@results) {
    warn "[INFO] No results returned for query.\n";
    exit 1;
}

print_results(@results);
