#!/usr/bin/perl
#

package LJ::Con;

use strict;
use vars qw(%cmd);

$cmd{'expunge_userpic'}->{'handler'} = \&expunge_userpic;
$cmd{'suspend'}->{'handler'} = \&suspend;
$cmd{'unsuspend'}->{'handler'} = \&suspend;
$cmd{'getemail'}->{'handler'} = \&getemail;
$cmd{'get_maintainer'}->{'handler'} = \&get_maintainer;
$cmd{'get_moderator'}->{'handler'} = \&get_moderator;
$cmd{'finduser'}->{'handler'} = \&finduser;
$cmd{'infohistory'}->{'handler'} = \&infohistory;
$cmd{'change_journal_status'}->{'handler'} = \&change_journal_status;
$cmd{'set_underage'}->{'handler'} = \&set_underage;

sub set_underage {
    my ($dbh, $remote, $args, $out) = @_;

    my $err = sub { push @$out, [ "error", shift ]; 0; };
    my $info = sub { push @$out, [ "info", shift ]; 1; };

    return $err->("This command takes three arguments.  Consult the reference for details.")
        unless scalar(@$args) == 4;
    return $err->("You don't have the necessary privilege (siteadmin:underage) to change an account's underage flag.")
        unless LJ::check_priv($remote, 'siteadmin', 'underage') || LJ::check_priv($remote, 'siteadmin', '*');

    my $u = LJ::load_user($args->[1]);
    return $err->("Invalid user.")
        unless $u;
    return $err->("Account is not a person type account.")
        unless $u->{journaltype} eq 'P';

    return $err->("Second argument must be 'on' or 'off'.")
        unless $args->[2] =~ /^(?:on|off)$/;
    my $on = $args->[2] eq 'on' ? 1 : 0;

    my $note = $args->[3];
    return $err->("You must provide a reason for this change as the third argument.")
        unless $note;

    # can't set state to what it is already
    return $err->("User is already of the requested underage state.")
        unless $on ^ $u->underage;

    my ($res, $sh, $status);
    if ($on) {
        $status = 'M'; # "M"anually turned on
        $res = "User marked as underage.";
        $sh = "marked; $note";
    } else {
        $status = undef; # no status change
        $res = "User no longer marked as underaged.";
        $sh = "unmarked; $note";
    }

    # now record this change (yes we log it twice)
    LJ::statushistory_add($u->{userid}, $remote->{userid}, "set_underage", $sh);
    $u->underage($on, $status, "manual");
    return $info->($res);
}

sub change_journal_status {
    my ($dbh, $remote, $args, $out) = @_;

    my $err = sub { push @$out, [ "error", shift ]; 0; };
    my $info = sub { push @$out, [ "info", shift ]; 1; };

    return $err->("This command takes two arguments.  Consult the reference for details.")
        unless scalar(@$args) == 3;
    return $err->("You don't have the necessary privilege (siteadmin:users) to change account status.")
        unless LJ::check_priv($remote, 'siteadmin', 'users') || LJ::check_priv($remote, 'siteadmin', '*');

    my $u = LJ::load_user($args->[1]);
    return $err->("Invalid user.")
        unless $u;

    # figure out the new status
    my $status = $args->[2];
    my $opts = {
        #name  =>   [ 'status-to', 'valid-statuses-from', 'error-message-if-from-fails', 'success-message' ]
        normal =>   [ 'V', 'ML', 'The user must be in memorial or locked status first.', 'User status set back to normal.' ],
        memorial => [ 'M', 'V', 'The user must be in normal status first.', 'User account set as memorial.' ],
        locked =>   [ 'L', 'V', 'The user must be in normal status first.', 'User account has been locked.' ],
    }->{$status};

    # make sure we got a valid $opts arrayref
    return $err->("Invalid status.  Consult the reference for more information.")
        unless defined $opts && ref $opts eq 'ARRAY';

    # verify user's from-statusvis is okay (it's contained in $opts->[1])
    return $err->($opts->[2]) unless $opts->[1] =~ /$u->{statusvis}/;

    # okay, so we need to update the user now and update statushistory
    LJ::statushistory_add($u->{userid}, $remote->{userid}, "journal_status", "Changed status to $status from $u->{statusvis}.");
    LJ::update_user($u->{'userid'}, { statusvis => $opts->[0], raw => 'statusvisdate=NOW()' });
    return $info->($opts->[3]);
}

sub expunge_userpic {
    my ($dbh, $remote, $args, $out) = @_;

    unless (scalar(@$args) == 3) {
        push @$out, [ "error", "This command takes exactly two arguments, username and picid.  Consult the reference." ];
        return 0;
    }

    my $user = $args->[1];
    my $picid = $args->[2]+0;

    unless (LJ::check_priv($remote, 'siteadmin', 'userpics') || LJ::check_priv($remote, 'siteadmin', '*')) {
        push @$out, [ "error", "You don't have access to expunge user picture icons." ];
        return 0;
    }

    my $u = LJ::load_user($user);

    # the actual expunging happens in ljlib
    my ($rval, $hookval) = LJ::expunge_userpic($u, $picid);
    push @$out, $hookval if @{$hookval || []};

    # now load up from the return value we got
    unless ($rval && $u) {
        push @$out, [ "error", "Error expunging user picture icon." ];
        return 0;
    }

    # but make sure to log it
    LJ::statushistory_add($u->{userid}, $remote->{userid}, 'expunge_userpic', "expunged userpic; id=$picid");
    push @$out, [ "info", "User picture icon $picid for $u->{user} expunged from $LJ::SITENAMESHORT." ];

    return 1;
}

sub suspend
{
    my ($dbh, $remote, $args, $out) = @_;

    my $confirmed = 0;
    if (scalar(@$args) == 4 && $args->[3] eq 'confirm') {
        pop @$args;
        $confirmed = 1;
    }

    unless (scalar(@$args) == 3) {
        push @$out, [ "error", "This command takes exactly 2 arguments.  Consult the reference." ];
        return 0;
    }

    my $cmd = $args->[0];
    my ($user, $reason) = ($args->[1], $args->[2]);

    if ($cmd eq "suspend" && $reason eq "off") {
        push @$out, [ "error", "The second argument to the 'suspend' command is no longer 'off' to unsuspend.  Use the 'unsuspend' command instead." ];
        return 0;
    }

    unless ($remote->{'priv'}->{'suspend'}) {
        push @$out, [ "error", "You don't have access to $cmd users." ];
        return 0;
    }

    # if the user argument is an email address...
    my @users;
    if ($user =~ /@/) {
        push @$out, [ "info", "Acting on users matching email $user..." ];

        my $dbr = LJ::get_db_reader();
        my $names = $dbr->selectcol_arrayref('SELECT user FROM user WHERE email = ?', undef, $user);
        if ($dbr->err) {
            push @$out, [ "error", "Database error: " . $dbr->errstr ];
            return 0;
        }
        unless ($names && @$names) {
            push @$out, [ "error", "No users found matching the email address $user." ];
            return 0;
        }

        # bail unless they've confirmed this mass action
        unless ($confirmed) {
            push @$out, [ "info", "    $_" ] foreach @$names;
            push @$out, [ "info", "To actually confirm this action, please do this again:" ];
            push @$out, [ "info", "    $cmd $user \"$reason\" confirm" ];
            return 1;
        }

        push @users, $_ foreach @$names;
    } else {
        push @users, $user;
    }

    foreach my $username (@users) {
        my $u = LJ::load_user($username);
        unless ($u) {
            push @$out, [ "error", "$username invalid/unable to load." ];
            next;
        }

        my $status = ($cmd eq "unsuspend") ? "V" : "S";
        if ($u->{'statusvis'} eq $status) {
            push @$out, [ "error", "$username was already in that state ($status)" ];
            next;
        }

        LJ::update_user($u->{'userid'}, { statusvis => $status, raw => 'statusvisdate=NOW()' });
        $u->{statusvis} = $status;

        LJ::statushistory_add($u->{'userid'}, $remote->{'userid'}, $cmd, $reason);

        LJ::Con::fb_push( $u );

        push @$out, [ "info", "User '$username' ${cmd}ed." ];
    }

    return 1;
}

sub getemail
{
    my ($dbh, $remote, $args, $out) = @_;

    unless (scalar(@$args) == 2) {
        push @$out, [ "error", "This command takes exactly 1 argument.  Consult the reference." ];
        return 0;
    }
    
    my ($user) = ($args->[1]);
    my $userid = &LJ::get_userid($user);

    unless ($remote->{'priv'}->{'suspend'}) {
        push @$out, [ "error", "You don't have access to see email addresses." ];
        return 0;
    }

    unless ($userid) {
        push @$out, [ "error", "Invalid user \"$user\"" ];
        return 0;
    }
    
    my $sth = $dbh->prepare("SELECT email, status FROM user WHERE userid=$userid");
    $sth->execute;
    my ($email, $status) = $sth->fetchrow_array;
    
    push @$out, [ "info", "User: $user" ];
    push @$out, [ "info", "Email: $email" ];
    push @$out, [ "info", "Status: $status  (A=approved, N=new, T=transferring)" ];

    return 1;
}

sub finduser
{
    my ($dbh, $remote, $args, $out) = @_;

    unless ($remote->{'priv'}->{'finduser'}) {
        push @$out, [ "error", "$remote->{'user'}, you are not authorized to use this command." ];
        return 0;
    }

    my ($crit, $data);
    if (scalar(@$args) == 2) {
        # new form; we can auto-detect emails easy enough
        $data = $args->[1];
        if ($data =~ /@/) {
            $crit = 'email';
        } else {
            # TODO: autodetect ip and userid...
            $crit = 'user';
        }
    } else {
        # old format...but new variation
        $crit = $args->[1];
        $data = $args->[2];

        # if they gave us a username and want to search by email, instead find
        # all users with that email address
        if ($crit eq 'email' && $data !~ /@/) {
            my $u = LJ::load_user($data);
            unless ($u) {
                push @$out, [ "error", "User doesn't exist." ];
                return 0;
            }

            $data = $u->{email};
        }
    }

    my $qd = $dbh->quote($data);

    my $userids;
    my $ip;

    if ($crit eq "email") {
        $userids = $dbh->selectcol_arrayref("SELECT userid FROM user WHERE email=$qd");
    } elsif ($crit eq "userid") {
        $userids = $dbh->selectcol_arrayref("SELECT userid FROM user WHERE userid=$qd");
    } elsif ($crit eq "user") {
        $userids = $dbh->selectcol_arrayref("SELECT userid FROM user WHERE user=$qd");
    } elsif ($crit eq "ip") {
        $userids = $dbh->selectcol_arrayref("SELECT userid FROM userlog WHERE action='account_create' AND ip=$qd");
        $ip = $data;
    } elsif ($crit eq "sameip") {
        $userids = $dbh->selectcol_arrayref("SELECT userid FROM user WHERE user=$qd");
        #like 'user'+'ip'  #continue...
    }


    if ($dbh->err) {
        push @$out, [ "error", "Error in database query: " . $dbh->errstr ];
        return 0;
    }
    unless ($userids && @$userids) {
        push @$out, [ "error", "No matches." ];
        return 0;
    }


    if ($crit eq "sameip") { #continue...
        my $res = $dbh->selectcol_arrayref("SELECT ip FROM userlog WHERE action='account_create' AND userid=$userids->[0]");
        $ip = $res->[0];
	$qd = $dbh->quote($ip);
	$userids = $dbh->selectcol_arrayref("SELECT userid FROM userlog WHERE action='account_create' AND ip=$qd");
    }


    my $us = LJ::load_userids(@$userids);
    foreach my $u (sort { $a->{userid} <=> $b->{userid} } values %$us) {

	unless ($ip) {
	   my $res = $dbh->selectcol_arrayref("SELECT ip FROM userlog WHERE action='account_create' AND userid=$u->{'userid'}");
	   $ip = $res->[0];
	}

        push @$out, [ "info", "User: $u->{'user'} ".
                      "($u->{'userid'}), journaltype: $u->{'journaltype'}, statusvis: $u->{'statusvis'}, $ip, email: ($u->{'status'}) $u->{'email'}" ];

        if ($u->underage) {
            my $reason;
            if ($u->underage_status eq 'M') {
                $reason = "manual set (see statushistory type set_underage)";
            } elsif ($u->underage_status eq 'Y') {
                $reason = "provided birthdate";
            } elsif ($u->underage_status eq 'O') {
                $reason = "unique cookie";
            }
            push @$out, [ "info", "  User is marked underage due to $reason." ];
        }

# no Paid accouns in LJR!
#        foreach (LJ::run_hooks("finduser_extrainfo", { 'dbh' => $dbh, 'u' => $u })) {
#            next unless $_->[0];
#            foreach (split(/\n/, $_->[0])) {
#                push @$out, [ "info", $_ ];
#            }
#        }
    }
    
    return 1;
}

sub get_maintainer
{
    my ($dbh, $remote, $args, $out, $edge) = @_;
    $edge ||= 'A';

    unless (scalar(@$args) == 2) {
        push @$out, [ "error", "This command takes exactly 1 argument.  Consult the reference." ];
        return 0;
    }
    
    unless ($remote->{'priv'}->{'finduser'}) {
        push @$out, [ "error", "$remote->{'user'}, you are not authorized to use this command." ];
        return 0;
    }

    my $user = $args->[1];
    my $u = LJ::load_user($user);

    unless ($u) {
        push @$out, [ "error", "Invalid user \"$user\"" ];
        return 0;
    }

    # journaltype eq 'P' means we're calling get_maintainer on a
    # plain user and we should get a list of what they maintain instead of
    # getting a list of what maintains them
    my $ids = $u->{journaltype} eq 'P' ?
              LJ::load_rel_target($u->{userid}, $edge) :
              LJ::load_rel_user($u->{userid}, $edge);
    $ids ||= [];

    # finduser loop
    finduser($dbh, $remote, ['finduser', 'userid', $_], $out) foreach @$ids;

    return 1;
}

sub get_moderator
{
    # simple pass through, but specify to use the 'M' edge
    return get_maintainer(@_, 'M');
}

sub infohistory
{
    my ($dbh, $remote, $args, $out) = @_;

    unless ($remote->{'privarg'}->{'finduser'}->{'infohistory'}) {
        push @$out, [ "error", "$remote->{'user'}, you are not authorized to use this command." ];
        return 0;
    }

    my $user = $args->[1];
    my $userid = LJ::get_userid($user);

    unless ($userid) {
        push @$out, [ "error", "Invalid user $user" ];
        return 0;
    }

    my $sth = $dbh->prepare("SELECT * FROM infohistory WHERE userid='$userid'");
    $sth->execute;
    if (! $sth->rows) {
        push @$out, [ "error", "Not much info found" ];
	my $sth1 = $dbh->prepare("select FROM_UNIXTIME(logtime) as logtime, ip from userlog where userid=? and action='account_create'");
	$sth1->execute($userid);
	my $acc = $sth1->fetchrow_hashref;
	push @$out, [ "info", "Account created at $acc->{'logtime'} from $acc->{'ip'} "];
    } else {
        push @$out, ["info", "Infohistory of user: $user"];
	my $sth1 = $dbh->prepare("select FROM_UNIXTIME(logtime) as logtime, ip from userlog where userid=? and action='account_create'");
	$sth1->execute($userid);
	if ($sth1->rows) {
	    my $acc = $sth1->fetchrow_hashref;
	    push @$out, [ "info", "Account created at $acc->{'logtime'} from $acc->{'ip'} "];
	}
	else {
	    push @$out, [ "error", "No account creation info found!" ];
	}
	
        while (my $info = $sth->fetchrow_hashref) {        
            $info->{'oldvalue'} ||= '(none)';
            push @$out, [ "info", 
                          "Changed $info->{'what'} at $info->{'timechange'}.\n".
                          "Old value of $info->{'what'} was $info->{'oldvalue'}.".
                          ($info->{'other'} ? 
                           "\nOther information recorded: $info->{'other'}" : "") ];
        }
    }
    return 1;
}

1;

