#**************************************************************************
#   Copyright (C) 2009 by Michael Andreen <harvATruinDOTnu>               *
#                                                                         *
#   This program is free software; you can redistribute it and/or modify  *
#   it under the terms of the GNU General Public License as published by  *
#   the Free Software Foundation; either version 2 of the License, or     *
#   (at your option) any later version.                                   *
#                                                                         *
#   This program is distributed in the hope that it will be useful,       *
#   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
#   GNU General Public License for more details.                          *
#                                                                         *
#   You should have received a copy of the GNU General Public License     *
#   along with this program; if not, write to the                         *
#   Free Software Foundation, Inc.,                                       *
#   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.         *
#**************************************************************************/

use strict;
use warnings;
use feature ':5.10';

use MooseX::Declare;
use NDIRC::Dispatcher;

command '+user' => {
	help => q(syntax: .+user username [pnick] | username must be alphanum characters, if no pnick is given then it will be set to the same as username),
	acl => 'irc_adduser'
}, class extends NDIRC::Command {
	method execute ($c,$msg) {
		my ($nick,$pnick) = $msg =~ /^(\w+)(?: ([^.\s]+))?$/ or die 'ARGS';
		$pnick //= $nick;

		my $dbh = $c->model;

		my $host = "$pnick.users.netgamers.org";
		my ($username,$hostname,$p_nick) = $dbh->selectrow_array(q{
SELECT username, hostmask,pnick
FROM users WHERE username = $1 OR hostmask = $2 OR pnick = $3
			},undef,$nick,$host,$pnick);

		if (defined $username){
			$c->reply("<b>$username ($p_nick)</b> already exists with host: <b>$hostname</b>");
		}else{
			$dbh->do(q{
INSERT INTO users (username,hostmask,pnick,password) VALUES(?,?,?,'')
				},undef,$nick,$host,$pnick);
			$c->reply("Added <b>$nick(/$pnick)</b> with host: <b>$host</b>");
		}
	}
};

command '-user' => {
	help => q(syntax: .-user nick | nick must be alphanum characters, if no pnick is given then it will be set to nick),
	acl => 'irc_deactivateuser'
}, class extends NDIRC::Command {
	method execute ($c,$msg) {
		my ($nick) = $msg =~ /^(\S+)$/ or die 'ARGS';

		my $dbh = $c->model;
		my $f = $dbh->prepare(q{SELECT uid,username FROM users WHERE username ILIKE ?});
		$f->execute($nick);
		my ($uid,$username) = $f->fetchrow();

		if ($f->rows == 1){
			my $updated = $dbh->do(q{
UPDATE users SET hostmask = ?, password = '' WHERE uid = ?
				},undef,$username,$uid);
			if ($updated > 0){
				my $groups = $dbh->do(q{DELETE FROM groupmembers WHERE uid = ?},undef,$uid);
				$groups += 0;
				$c->reply("<b>$username</b> has been deactivated. Removed from $groups groups.");
			}else{
				$c->reply("Something went wrong when trying to modify <b>$username</b>");
			}
		}elsif ($f->rows == 0){
			$c->reply("No hit, maybe spelling mistake, or add % as wildcard");
		}else{
			$c->reply("More than 1 user matched, please refine the search");
		}
		$f->finish;
	}
};

command chattr => {
	help => q(syntax: .chattr username [-]flags | % can be used for wildcards \%arro% will match barrow, if a - is given then flags will be removed, otherwise added),
	acl => 'irc_chattr'
}, class extends NDIRC::Command {
	method execute ($c,$msg) {
		my ($nick, $mode, $flags) = $msg =~ /^(\S+) (\+|-)?(\w+)$/ or die 'ARGS';
		my $dbh = $c->model;

		my $f = $dbh->prepare(q{SELECT uid,username FROM users WHERE username ILIKE ?});
		$f->execute($nick);
		my $user = $f->fetchrow_hashref;
		if ($f->rows == 1){
			my $update;
			if ($mode ~~ '-'){
				$update = $dbh->prepare(q{
DELETE FROM groupmembers WHERE uid = $1 AND gid = ANY($2)
					});
			}else{
				$update = $dbh->prepare(q{
INSERT INTO groupmembers (uid,gid)
	(SELECT $1,gid FROM unnest($2::text[]) gid WHERE
		gid NOT IN (SELECT gid FROM groupmembers WHERE uid = $1)
		AND gid IN (SELECT gid FROM groups))
				});
			}
			my @flags = split /\W*/,$flags;
			$update->execute($user->{uid},\@flags);
			$update = $dbh->prepare(q{
SELECT array_to_string(array_agg(gid),'')
FROM (SELECT uid,gid FROM groupmembers ORDER BY uid,gid ) g
WHERE uid = ?
				});
			$flags = $dbh->selectrow_array($update,undef,$user->{uid});
			$c->reply("Flags for <b>$user->{username}</b> are now: $flags");
		}elsif ($f->rows == 0){
			$c->reply("No hit, maybe spelling mistake, or add % as wildcard");
		}else{
			$c->reply("More than 1 user matched, please refine the search");
		}
		$f->finish;
	}
};

command flags => {
	help => q(Lists all the available flags),
	acl => 'irc_whois'
}, class extends NDIRC::Command {
	method execute ($c,$msg) {
		my $dbh = $c->model;
		my ($flags) = $dbh->selectrow_array(q{
SELECT array_to_string(array_agg(gid||':'||groupname),', ') FROM groups
			});
		$c->reply("Current flags: $flags");
	}
};

command whois => {
	help => q(syntax: .whois username | % can be used for wildcards \%arro% will match barrow.),
	acl => 'irc_whois'
}, class extends NDIRC::Command {
	method execute ($c,$msg) {
		my ($nick) = $msg =~ /^(\S+)$/ or die 'ARGS';
		my $dbh = $c->model;


		my $f = $dbh->prepare(q{
SELECT username, pnick, hostmask, array_to_string(array_agg(gid),'') AS flags
FROM users u
	LEFT OUTER JOIN (SELECT uid,gid FROM groupmembers ORDER BY uid,gid
	) g USING (uid)
WHERE username ILIKE ?
GROUP BY username,pnick,hostmask LIMIT 5
			});
		$f->execute($nick);
		while (my $user = $f->fetchrow_hashref){
			$c->reply("<b>$user->{username} (/$user->{pnick})</b> flags: ($user->{flags}) host: $user->{hostmask}");
		}
		if ($f->rows == 0){
			$c->reply("No hit, maybe spelling mistake, or add % as wildcard");
		}
	}
};

command flag => {
	help => q(syntax: .flag flag [-noflags] | Lists all users with the given flag. Can specify a second argument to remove members that has any of those flags. .flag M -C lists all members that doesn't have community flag),
	acl => 'irc_flag'
}, class extends NDIRC::Command {
	method execute ($c,$msg) {
		my ($flag,$noflag) = $msg =~ /^(\w)(?: -(\w+))?$/ or die 'ARGS';
		$noflag //= '';

		my $f = $c->model->prepare(q{
SELECT array_to_string(array_agg(username),', '),count(username)
FROM (SELECT uid, username FROM users ORDER BY username) u
	JOIN groupmembers gm USING (uid)
WHERE gid = $1 AND uid NOT IN (SELECT uid FROM groupmembers WHERE gid =  ANY($2))
			});
		my @noflag = split //, $noflag;
		my ($users,$count) = $c->model->selectrow_array($f,undef,$flag,\@noflag);
		$c->reply("<b>$count</b> Users with flag <b>$flag</b>: $users");
	}
};

command laston => {
	help => q(syntax: .laston flag [days] | lists users and the number of days since they were last seen (irc|forum|claim). If days is specified it will only list users with at least that amount of idle time. Days can also specify forum and claim with irc|forum|claim syntax),
	acl => 'irc_laston'
}, class extends NDIRC::Command {
	method execute ($c,$msg) {
		my ($flag,$min,$forum,$claim) = $msg =~ /^(\w)(?: (\d+)(?:\|(\d+)\|(\d+))?)?$/
			or die 'ARGS';
		$min //= 0;

		my $f = $c->model->prepare(q{
SELECT username, COALESCE(last::text,'?') AS last
	,COALESCE(lastforum::text,'?') AS lastforum
	,COALESCE(lastclaim::text,'?') AS lastclaim
FROM (SELECT username
		,date_part('day',now() - laston)::int AS last
		,date_part('day',now() -
			(SELECT max(time) FROM forum_thread_visits WHERE uid = u.uid)) AS lastforum
		,date_part('day',now() -
			(SELECT max(timestamp) FROM raid_claims WHERE uid = u.uid)) AS lastclaim
	FROM users u
		NATURAL JOIN groupmembers
	WHERE gid = $1
	) a
WHERE COALESCE(last >= $2,TRUE) AND COALESCE(lastforum >= $3,TRUE)
	AND COALESCE(lastclaim >= $4,TRUE)
ORDER BY a.last DESC, a.lastforum DESC, a.lastclaim DESC
			});
		$f->execute($flag,$min,$forum,$claim);

		my $text;
		my $i = 0;
		while (my $user = $f->fetchrow_hashref){
			$text .= "$user->{username}($user->{last}|$user->{lastforum}|$user->{lastclaim}) ";
			$i++;
		}
		$c->reply("<b>$i</b> Users(days) with flag <b>$flag</b>: $text");
	}
};


command lastseen => {
	help => q(syntax: .lastseen username | Shows the number of days since the user(s) last visited irc, forum and raids),
	acl => 'irc_lastseen'
}, class extends NDIRC::Command {
	method execute ($c,$msg) {
		my ($username) = $msg =~ /^(\S+)$/ or die 'ARGS';

		my $f = $c->model->prepare(q{
SELECT username, COALESCE(date_part('day',now() - laston)::text,'?') AS last
	,COALESCE(date_part('day',now() - (SELECT max(time)
		FROM forum_thread_visits WHERE uid = u.uid))::text,'?') AS lastforum
	,COALESCE(date_part('day',now() - (SELECT max(timestamp)
		FROM raid_claims WHERE uid = u.uid))::text,'?') AS lastclaim
FROM users u
WHERE username ILIKE $1 ORDER BY username
			});
		$f->execute($username);

		my $text;
		my $i = 0;
		while (my $user = $f->fetchrow_hashref){
			$text .= "$user->{username}($user->{last}|$user->{lastforum}|$user->{lastclaim}) ";
			$i++;
		}
		$c->reply("<b>$i</b> Users(days): $text");
	}
}

command getships => {
	help => q(Usage: .getships ship | % can be used as wildcard, e.g. beet%, shipshome shows the number of ships currently home),
	alias => 'shipshome',
	acl => 'irc_getships'
}, class extends NDIRC::Command {
	method execute ($c,$msg) {
		my ($ship) = $msg =~ /^(\S+)$/ or die 'ARGS';
		my $dbh = $c->model;

		my $f = $dbh->prepare(q{
SELECT username,SUM(fs.amount) AS amount
FROM users u
	JOIN (SELECT DISTINCT ON (pid) pid,fid FROM fleets
		WHERE mission = 'Full fleet' AND name <> 'Unit'
		ORDER BY pid,tick DESC,fid DESC
	) f  USING (pid)
	JOIN fleet_ships fs USING (fid)
WHERE ship ILIKE $1 AND uid IN (SELECT uid FROM groupmembers WHERE gid = 'M')
GROUP BY username ORDER BY amount DESC
			});
		if ($self->name eq 'shipshome'){
			$f = $dbh->prepare(q{
SELECT username,SUM(amount) AS amount
FROM available_ships
WHERE ship ILIKE ? AND uid IN (SELECT uid FROM groupmembers WHERE gid = 'M')
GROUP BY username ORDER BY amount DESC
				});
		}
		$f->execute($ship);
		my $text;
		my $i = 0;
		my $total = 0;
		while (my $user = $f->fetchrow_hashref){
			$text .= "$user->{username}: $user->{amount} ";
			$i++;
			$total += $user->{amount};
		}
		if ($text){
			$c->reply("<b>$i</b> Users with <b>$total $ship</b>: $text");
		}else{
			$c->reply("Couldn't find any user with <b>$ship</b>");
		}
	}
};

command getfleet => {
	help => q(Usage: .getfleet username | % can be used as wildcard, e.g. barr%),
	acl => q(irc_getfleet)
}, class extends NDIRC::Command {
	method execute ($c,$msg) {
		my ($nick) = $msg =~ /^(\S+)$/ or die 'ARGS';
		my $dbh = $c->model;

		my $f = $dbh->prepare(q{
SELECT fs.ship, fs.amount, username
FROM fleet_ships fs
	JOIN (SELECT fid,username
		FROM fleets f
			JOIN users u USING (pid)
		WHERE mission = 'Full fleet' AND name <> 'Unit'
			AND username ILIKE $1
		ORDER BY pid,tick DESC,fid DESC
		LIMIT 1
	) f  USING (fid)
ORDER BY num
			});
		$f->execute($nick);
		my $text;
		my $username;
		while (my $ship = $f->fetchrow_hashref){
			unless (defined $username) {
				$username = $ship->{username};
				$text = "<b>$username</b> has: "
			}
			$text .= "$ship->{ship} $ship->{amount} ";
		}
		if ($text){
			$c->reply($text);
		}else{
			$c->reply("Couldn't find any fleet for $nick");
		}
	}
};

command sethost => {
	help => q(Usage: .sethost username [host] | if host isn't given then it resets to netgamers host),
	acl => q(irc_sethost)
}, class extends NDIRC::Command {
	method execute ($c,$msg) {
		my ($nick,$host) = $msg =~ /^(\S+)(?: (\S+))?$/ or die 'ARGS';
		my $dbh = $c->model;

		my $f = $dbh->prepare(q{
SELECT uid,username,pnick,hostmask FROM users WHERE username ILIKE ?
			});
		$f->execute($nick);
		my $user = $f->fetchrow_hashref;
		if ($f->rows == 1){
			$host //= "$user->{pnick}.users.netgamers.org";
			eval{
				$dbh->do(q{UPDATE users SET hostmask = ? WHERE uid = ?}
					,undef,$host,$user->{uid});
				$c->reply("Updated <b>$user->{username}</b>'s host to: <b>$host</b>");
			};
			if($@){
				if ($@ =~ /duplicate key value violates unique constraint/){
					my ($username, $hostname) = $dbh->selectrow_array(q{
SELECT username,hostmask FROM users WHERE hostmask = $1
						},undef,$host);
					$c->reply("<c04>Problem</c>, <b>$username</b> already uses host <b>$hostname</b>.");
				}else{
					die $@;
				}
			}
		}elsif ($f->rows == 0){
			$c->reply("No hit, maybe spelling mistake, or add % as wildcard");
		}else{
			$c->reply("More than 1 user matched, please refine the search");
		}
		$f->finish;
	}
};

command setpnick => {
	help => q(Usage: .setpnick username pnick | Changes a user's pnick),
	acl => q(irc_setpnick)
}, class extends NDIRC::Command {
	method execute ($c,$msg) {
		my ($nick,$pnick) = $msg =~ /^(\S+) (\S+)$/ or die 'ARGS';
		my $dbh = $c->model;

		my $f = $dbh->prepare(q{SELECT uid,username FROM users WHERE username ILIKE ?});
		$f->execute($nick);
		my $user = $f->fetchrow_hashref;
		if ($f->rows == 1){
			my $hostname = "$pnick.users.netgamers.org";
			eval{
				$dbh->do(q{UPDATE users SET pnick = ?, hostmask = ? WHERE uid = ?}
					,undef,$pnick,$hostname,$user->{uid});
				$c->reply("Updated <b>$user->{username}</b>'s pnick to: <b>$pnick</b> and hostname to <b>$hostname</b>");
			};
			if($@){
				if ($@ =~ /duplicate key value violates unique constraint/){
					my ($username, $hostname, $pnick) = $dbh->selectrow_array(q{
SELECT username,hostmask,pnick FROM users WHERE hostmask = $1 OR pnick = $2
						},undef,$hostname, $pnick);
					$c->reply("<c04>Problem</c>, <b>$username</b> already uses host <b>$hostname</b> and pnick <b>$pnick</b>.");
				}else{
					die $@;
				}
			}
		}elsif ($f->rows == 0){
			$c->reply("No hit, maybe spelling mistake, or add % as wildcard");
		}else{
			$c->reply("More than 1 user matched, please refine the search");
		}
		$f->finish;
	}
};

my $points = class extends NDIRC::Command {
	has point => (
		is => 'ro',
		isa => 'Str',
		required => 1
	);
	method execute ($c,$msg) {
		my ($nick,$points) = $msg =~ /^(\S+)(?: (-?(:?\d+|\d*\.\d+)))?$/ or die 'ARGS';

		return unless $self->check($c,$nick,$points);

		$points //= 1;

		my $dbh = $c->model;

		if ($points*$points > 400){
			$c->reply("Values between -20 and 20 please");
			return;
		}

		my $f = $dbh->prepare(q{SELECT uid,username FROM users WHERE username ILIKE ?});
		$f->execute($nick);
		my $user = $f->fetchrow_hashref;
		if ($f->rows == 1){
			my $type .= $self->point . "_points";
			$dbh->do(qq{UPDATE users SET $type = $type + ? WHERE uid = ?}
				,undef,$points,$user->{uid});
			$c->reply("$user->{username} has been given $points $type");
		}elsif ($f->rows == 0){
			$c->reply("No hit, maybe spelling mistake, or add % as wildcard");
		}else{
			$c->reply("More than 1 user matched, please refine the search");
		}
		$f->finish;
	}
	method check ($c,$nick,$points) {
		return 1;
	}
};

command a => {
	help => q(Usage: .a username [points] | % can be used for wildcards %arro% will match barrow, if the number of points isn't specified, then 1 will be assumed.),
	point => 'attack',
	acl => q(irc_a)
}, class {
	extends $points->name;
	method check ($c,$nick,$points) {
		my ($fleets) = $c->model->selectrow_array(q{
SELECT count(*) FROM raids r
	JOIN raid_targets rt ON r.id = rt.raid
	JOIN raid_claims rc ON rt.id = rc.target
WHERE not launched  AND tick + 24 > tick()
	AND uid = (SELECT uid FROM users WHERE username ILIKE $1);
			},undef,$nick);

		if ($fleets > 0 && $points > 0){
			$c->reply("$nick has $fleets claimed waves the last 24 ticks that aren't marked as launched, so no points.");
			return 0;
		}
		return 1;
	}
};

command d => {
	help => q(Usage: .d username [points] | % can be used for wildcards %arro% will match barrow, if the number of points isn't specified, then 1 will be assumed.),
	acl => q(irc_d),
	point => 'defense',
	type => q(def)
}, $points;

command s => {
	help => q(Usage: .s username [points] | % can be used for wildcards %arro% will match barrow, if the number of points isn't specified, then 1 will be assumed.),
	point => 'scan',
	acl => q(irc_s),
}, $points;

command h => {
	help => q(Usage: .h username [points] | % can be used for wildcards %arro% will match barrow, if the number of points isn't specified, then 1 will be assumed.),
	point => 'humor',
	acl => q(irc_h),
}, $points;

command getanti => {
	help => q(Usage: .getanti class [eta] | class is the target class, like fi or bs, eta is optional max eta for the ships.),
	acl => q(irc_getships),
}, class extends NDIRC::Command {
	method execute ($c,$msg) {
		my ($class,$eta) = $msg =~ /^(\S+)(?: (\d+))?$/ or die 'ARGS';
		my $dbh = $c->model;

		$eta //= 9;

		my @classes = qw/Fi Co Fr De Cr Bs/;
		for (@classes){
			if (lc $class eq lc $_){
				$class = $_;
				last;
			}
		}

		while ($eta < 9){
			pop @classes;
			pop @classes;
			$eta++;
		}

		my $f = $dbh->prepare(q{
SELECT username, ship, amount, CASE WHEN $1 = t1 THEN 't1' ELSE 't2' END AS t
FROM available_ships a
	JOIN ship_stats s ON (a.ship = s.name)
WHERE uid IN (SELECT uid FROM groupmembers WHERE gid = 'M')
	AND class = ANY($2)
	AND $1 IN (t1,t2)
ORDER BY amount*(metal+crystal+eonium)*CASE WHEN $1 = t1 THEN 1.0 ELSE 0.6 END DESC
			});
		$f->execute($class,\@classes);
		my $text;
		my $i = 0;
		my $total = 0;
		while (my $user = $f->fetchrow_hashref){
			$text .= "($user->{username}: $user->{ship} $user->{amount} $user->{t}) ";
			$i++;
			$total += $user->{amount};
		}
		if ($text){
			$c->reply("<b>$i</b> fleets with anti-<b>$class</b>: $text");
		}else{
			$c->reply("Couldn't find any user with anti-<b>$class</b>");
		}
	}
};

1;
