#!/usr/bin/env perl

use strict;
use warnings;
use utf8;

#use Smart::Comments;
use FindBin;
use lib "$FindBin::Bin/../lib";
use OpenResty::Dispatcher;
use OpenResty::Limits;
use Getopt::Std;
use Time::HiRes;

my $cmd = lc(shift) || $ENV{OPENRESTY_COMMAND};

if ($ENV{REQUEST_URI}) {
    if (!$cmd) {
        $cmd = 'fastcgi';
        $ENV{OPENRESTY_COMMAND} = $cmd;
    }
}

$cmd ||= '';

eval {
    OpenResty::Dispatcher->init($cmd);
};
warn $@ if $@;

sub do_stats {
    my ($begin_time) = @_;
    if (my $stats_dir = $OpenResty::Dispatcher::StatsLog) {
        my $diff = Time::HiRes::time - $begin_time;
        my $now = localtime;
        my ($sec, $min, $hour, $mday, $mon, $year) = localtime;
        $year += 1900; $mon += 1;
        #my $now = "$year-$mon-$mday $hour:$min:$sec";
        #my ($sec, $min, $hour, $mday, $mon, $year) = localtime;
        #$year += 1900; $mon += 1;
        my $stats_file = sprintf("$stats_dir/%04d-%02d-%02d.log", $year, $mon, $mday);
        open my $log, ">>$stats_file" or die "Can't open stats_log file $stats_file for writing: $!\n";
        my $client = $ENV{REMOTE_ADDR};
        my $meth = $ENV{REQUEST_METHOD};
        my $url = $ENV{REQUEST_URI};
        ### %ENV
        print $log sprintf("[%04d-%02d-%02d %02d:%02d:%02d] %.03f %s \"%s %s\"\n", $year, $mon, $mday, $hour, $min, $sec, $diff, $client, $meth, $url);
        close $log;
    }
}

if ($cmd eq 'fastcgi') {
    require OpenResty::FastCGI;
    while (my $cgi = new OpenResty::FastCGI) {
        my $begin_time;
        if (defined $OpenResty::Dispatcher::StatsLog) {
            $begin_time = Time::HiRes::time;
        }

        eval {
            OpenResty::Dispatcher->process_request($cgi);
        };
        if ($@) {
            warn $@;
            print "HTTP/1.1 200 OK\n";
            # XXX don't show $@ to the end user...
            print qq[{"success":0,"error":"$@"}\n];
        }
        eval {
            do_stats($begin_time);
        };
        if ($@) { warn $@; }
    }
    exit;
} elsif ($cmd eq 'cgi') {
    require CGI::Simple;
    my $cgi = CGI::Simple->new;
    OpenResty::Dispatcher->process_request($cgi);
    exit;
} elsif ($cmd eq 'start') {
    my %opts;
    getopts('p:', \%opts);
    my $port = $opts{p} || 8000;

    require OpenResty::Server;
    my $server = OpenResty::Server->new;
    $server->port($port);
    $server->run;
    exit;
} elsif ($cmd eq 'fastcgi2') { # with PL/Proxy support
}

my $error = $OpenResty::Dispatcher::InitFatal;
if ($error) {
    die $error;
}
my $backend = $OpenResty::Backend;

if ($cmd eq 'adduser') {
    my $user = shift or
        die "No user specified.\n";
    if ($backend->has_user($user)) {
        die "User $user already exists.\n";
    }
    eval "use Term::ReadKey;";
    if ($@) { die $@; }
    #local $| = 1;

    my $password;
    print "Enter the password for the Admin role: ";

    ReadMode(2);
    my $key;
    while (not defined ($key = ReadLine(0))) {
    }
    ReadMode(0);

    $key =~ s/\n//s;
    print "\n";

    my $saved_key = $key;
    #warn "Password: $password\n";
    OpenResty::check_password($saved_key);

    print "Re Enter the password for the Admin role: ";

    ReadMode(2);
    while (not defined ($key = ReadLine(0))) {
    }
    ReadMode(0);

    $key =~ s/\n//s;
    print "\n";

    if ($key ne $saved_key) {
        die "2 passwords don't match.\n";
    }
    $password = $key;

    $OpenResty::Backend->add_user($user, $password);
    my $machine = $OpenResty::Backend->has_user($user);
    if ($machine) {
        warn "User $user created on node $machine.\n";
    }
} elsif ($cmd eq 'deluser') {
    my $user = shift or
        die "No user specified.\n";
    if ($backend->has_user($user)) {
        $OpenResty::Backend->drop_user($user);
    } else {
        die "User $user does not exist.\n";
    }
} elsif ($cmd eq 'upgrade') {
    my $user = shift;
    require OpenResty::Script::Upgrade;
    OpenResty::Script::Upgrade->go($backend, $user);
} elsif ($cmd eq '' or $cmd eq 'shell') {
    require OpenResty::Shell;
    my $shell = OpenResty::Shell->new($backend);
    $shell->run;
} elsif ($cmd eq 'updatekey') {
	my $secret=shift||$backend->random_secret();

	die "Invalid captcha secret, must be exactly 16 bytes long and contains only characters in [0-9a-zA-Z].\n"
		unless $backend->is_valid_captcha_secret($secret);

	# the secret format check is done before the updating occurs
	unless($backend->update_captcha_secret($secret)) {
		die "Failed to update captcha secret.\n"
	} else {
		print "Captcha secret updated.\n";
	}
} elsif ($cmd eq 'compile') {
    require OpenResty::Script::Compile;
    OpenResty::Script::Compile->go(\@ARGV);
} else {
    die "Unknown command: $cmd\n";
}
__END__

=head1 NAME

openresty - Command-line frontend utility for the OpenResty server

=head1 SYNOPSIS

    # run the OpenResty server via the standalone HTTP server
    #   provided by HTTP::Server::Simple:
    $ bin/openresty start

    # run the OpenResty server in plain old CGI mode:
    $ bin/openresty cgi

    # or equivalently:
    $ OPENRESTY_COMMAND=cgi bin/openresty

    # run the OpenResty server in FastCGI mode:
    $ bin/openresty fastcgi

    # or equivalently:
    $ OPENRESTY_COMMAND=fastcgi bin/openresty

    # enter the OpenResty shell:
    $ bin/openresty

    # add a new OpenResty account named foo
    $ bin/openresty adduser foo

    # remove an existing OpenResty account named bar
    $ bin/openresty deluser bar

    # upgrade the metamodel in the DB to the latest version
    $ bin/openresty upgrade

    # upgrade the metamodel of (only) the OpenResty account (foo) specified:
    $ bin/openresty upgrade foo

=head1 DESCRIPTION

This is the command-line frontend for the OpenResty server. The C<openresty> script can server as a

=over

=item *

OpenResty account adminstration tool (i.e., creating accounts, removing accounts, listing accounts, and etc.).

=item *

OpenResty server entry point (via CGI, FastCGI, or standalone server).

=item *

A psql-like shell tools for administrating the backend databases (either Pg or PgFarm).

=item *

A metamodel upgrading tool. (See L<OpenResty::Spec::MetaModel> for more details about the metamodel.)

=back

=head1 AUTHOR

Agent Zhang (agentzh) C<< <agentzh@yahoo.cn> >>

=head1 License and Copyright

Copyright (c) 2007, 2008 by Yahoo! China EEEE Works, Alibaba Inc.

This module is free software; you can redistribute it and/or
modify it under the Artistic License 2.0.
A copy of this license can be obtained from

L<http://opensource.org/licenses/artistic-license-2.0.php>

=head1 SEE ALSO

L<OpenResty>, L<OpenResty::Spec::Upgrading>, L<OpenResty::Spec::AccountAdmin>, L<OpenResty::Spec::Installation>, L<OpenResty::Spec::Overview>, L<OpenResty::Spec::REST_cn>.

