package Convos::Controller::Client;

=head1 NAME

Convos::Controller::Client - Mojolicious controller for IRC chat

=cut

use Mojo::Base 'Mojolicious::Controller';
use Mojo::JSON 'j';
use Convos::Core::Util qw/ as_id id_as /;
use constant TEST_IS_CHANNEL => $ENV{TEST_IS_CHANNEL} || 0;
use constant DEBUG           => $ENV{CONVOS_DEBUG}    || 0;

my $N_MESSAGES = $ENV{N_MESSAGES} || 30;

=head1 METHODS

=head2 route

Route to last seen IRC conversation.

=cut

sub route {
  my $self  = shift;
  my $login = $self->session('login');

  if ($login) {
    return $self->redirect_last($login);
  }

  $self->delay(
    sub {
      my ($delay) = @_;
      $self->redis->scard(users => $delay->begin);
    },
    sub {
      my ($delay, $n) = @_;
      $self->redirect_to($n ? 'login' : 'register');
    },
  );
}

=head2 conversation

Used to render the main IRC client conversation.

=cut

sub conversation {
  my $self    = shift;
  my $login   = $self->session('login');
  my $network = $self->stash('network');
  my $target  = $self->stash('target') || '';
  my $name    = as_id $network, $target;
  my $redis   = $self->redis;

  $self->res->headers->header('X-Is-Channel', $self->stash('is_channel') || 0);
  $self->stash(from_archive => 0, target => $target, state => 'connected');

  $self->delay(
    sub {
      my ($delay) = @_;
      my $nid = $self->param('nid') || undef;

      # make sure conversations exists before doing zadd
      $redis->zscore("user:$login:conversations", $name, $delay->begin);
      $redis->zrevrange("user:$login:conversations", 0, 1, $delay->begin);
      $self->_modify_notification($nid, read => 1, sub { }) if defined $nid;
    },
    sub {
      my ($delay, $last_read_time, $previous_name) = @_;

      $self->stash(last_read_time => $self->param('last-read-time') || $last_read_time || 0);

      if ($target and !$last_read_time) {    # no such conversation
        return $delay->begin(0)->([$login, $login, 'connected']) if $self->param('from');
        return $self->stash(layout => 'tactile')->render_not_found;
      }
      if (!$target) {
        $self->stash(sidebar => 'convos');
      }
      if ($network eq 'convos') {
        return $delay->begin(0)->([$login, 'connected']);
      }
      if ($last_read_time) {
        $redis->zadd("user:$login:conversations", time, $previous_name->[0]) unless $name eq $previous_name->[0];
        $redis->zadd("user:$login:conversations", time + 0.01, $name);
      }

      $redis->hmget("user:$login:connection:$network", qw( current_nick nick state ), $delay->begin);
    },
    sub {
      my $delay        = shift;
      my $current_nick = shift @{$_[0]};
      my $wanted_nick  = shift @{$_[0]};
      my $state        = shift @{$_[0]};

      $state ||= 'disconnected';
      $self->stash(current_nick => $current_nick || $wanted_nick, state => $state);
      $self->_conversation($delay->begin);
      $self->conversation_list($delay->begin);
      $self->notification_list($delay->begin) if $self->stash('full_page');
    },
    sub {
      my ($delay, $conversation, $conversation_list, $notification_list) = @_;
      $self->render(conversation => $conversation || []);
    },
  );
}

=head2 command_history

Render the command history.

=cut

sub command_history {
  my $self = shift;
  my $login = $self->session('login') || '';

  $self->redis->lrange(
    "user:$login:cmd_history",
    0, -1,
    sub {
      $self->render(json => $_[1] || []);
    }
  );
}

=head2 clear_notifications

Will mark all notifications as read.

=cut

sub clear_notifications {
  my $self  = shift;
  my $login = $self->session('login');

  $self->delay(
    sub {
      my ($delay) = @_;
      $self->redis->lrange("user:$login:notifications", 0, 100, $delay->begin);
    },
    sub {
      my ($delay, $notification_list) = @_;
      my $i = 0;

      while ($i < @$notification_list) {
        my $notification = j $notification_list->[$i];
        $notification->{read}++;
        $self->redis->lset("user:$login:notifications", $i, j $notification);
        $i++;
      }

      $self->render(json => {cleared => $i});
    }
  );
}

=head2 notifications

Render L<Convos::Plugin::Helpers/notification_list> as HTML or JSON.

=cut

sub notifications {
  my $self = shift;

  $self->delay(
    sub {
      my ($delay) = @_;

      $self->notification_list($delay->begin);
      $self->_modify_notification($self->param('nid'), read => 1, $delay->begin) if length $self->param('nid');
    },
    sub {
      my ($delay, $notification_list) = @_;

      $self->respond_to(json => {json => $notification_list}, html => {template => 'sidebar/notification_list'},);
    },
  );
}

sub _conversation {
  my ($self, $cb) = @_;
  my $login   = $self->session('login');
  my $network = $self->stash('network');
  my $target  = $self->stash('target');
  my $key     = $target ? "user:$login:connection:$network:$target:msg" : "user:$login:connection:$network:msg";

  if (my $to = $self->param('to')) {    # to a timestamp
    $self->stash(from_archive => 1);
    $self->redis->zrevrangebyscore(
      $key => $to,
      '-inf',
      'WITHSCORES',
      LIMIT => 0,
      $N_MESSAGES,
      sub {
        my $list = pop || [];
        $self->format_conversation(
          sub {
            my $timestamp = pop @$list;
            my $message = j(pop @$list) or return;
            $message->{timestamp} = $timestamp;
            $message;
          },
          $cb
        );
      }
    );
  }
  elsif (my $from = $self->param('from')) {    # from at timestamp
    $self->stash(from_archive => 1);
    $self->redis->zrangebyscore(
      $key => $from,
      '+inf',
      'WITHSCORES',
      LIMIT => 0,
      $N_MESSAGES + 1,
      sub {
        my $list = pop || [];
        $self->stash(got_more => @$list / 2 > $N_MESSAGES);
        $self->format_conversation(
          sub {
            my $current = shift @$list or return;
            my $message = j $current;
            @$list or return;    # skip the last
            $message->{timestamp} = shift @$list;
            $message;
          },
          $cb,
        );
      }
    );
  }
  else {                         # default
    $self->redis->zcard(
      $key,
      sub {
        my ($redis, $end) = @_;
        my $start = $end > $N_MESSAGES ? $end - $N_MESSAGES : 0;
        $redis->zrange(
          $key => $start,
          $end,
          'WITHSCORES',
          sub {
            my $list = pop || [];
            $self->format_conversation(
              sub {
                my $message = j(shift @$list) or return;
                $message->{timestamp} = shift @$list;
                $message;
              },
              $cb,
            );
          }
        );
      }
    );
  }
}

sub _modify_notification {
  my ($self, $id, $key, $value, $cb) = @_;
  my $login     = $self->session('login');
  my $redis_key = "user:$login:notifications";

  $self->redis->lindex(
    $redis_key,
    $id,
    sub {
      my $redis = shift;
      my $notification = shift or return $redis->$cb(0);
      $notification = j $notification;
      $notification->{$key} = $value;
      $redis->lset($redis_key, $id, j($notification), $cb);
    }
  );
}

=head1 COPYRIGHT

See L<Convos>.

=head1 AUTHOR

Jan Henning Thorsen

Marcus Ramberg

=cut

1;