package Convos::Chat;

=head1 NAME

Convos::Chat - Mojolicious controller for IRC chat

=cut

use Mojo::Base 'Mojolicious::Controller';
use Mojo::JSON 'j';
use Convos::Core::Commands;
use constant PING_INTERVAL => $ENV{CONVOS_PING_INTERVAL} || 30;
use constant DEFAULT_RESPONSE => "Hey, I don't know how to respond to that. Try /help to see what I can so far.";

=head1 METHODS

=head2 socket

Handle conversation exchange over websocket.

=cut

sub socket {
  my $self = shift;
  my $login = $self->session('login');
  my $key = "convos:user:$login:out";
  my($sub, $tid);

  Mojo::IOLoop->stream($self->tx->connection)->timeout(PING_INTERVAL * 2);
  Scalar::Util::weaken($self);

  # send ping frames
  $tid = Mojo::IOLoop->recurring(PING_INTERVAL, sub {
           $self->send('<div class="ping"/>');
         });

  # from browser to backend
  $self->on(
    message => sub {
      my ($self, $octets) = @_;
      my $dom = Mojo::DOM->new($octets)->at('div');
      $self->logf(debug => '[ws] < %s', $octets);

      if($dom and $dom->attr('class') eq 'pong') {
        return;
      }
      elsif($dom and $dom->{'id'} and $dom->{'data-server'}) {
        @$dom{qw( server target uuid )} = map { delete $dom->{$_} || '' } qw( data-server data-target id );
        $self->_handle_socket_data($dom);
      }
      else{
        $self->_send_400($dom, "Invalid message ($octets)")->finish;
      }
    }
  );
  $self->on(
    finish => sub {
      my $self = shift or return;
      Mojo::IOLoop->remove($tid);
      delete $self->stash->{$_} for qw( sub redis );
    }
  );

  # from backend to browser
  $sub = $self->stash->{sub} = $self->redis->subscribe($key);
  $sub->on(
    error => sub {
      $self->logf(warn => 'sub: %s', pop);
      $self->finish;
    }
  );
  $sub->on(
    message => sub {
      my $sub = shift;
      my @messages = (shift);

      $self->logf(debug => '[%s] > %s', $key, $messages[0]);
      $self->format_conversation(
        sub { j(shift @messages) },
        sub {
          my($self, $messages) = @_;
          $self->send_partial("event/$messages->[0]{event}", target => '', %{ $messages->[0] });
        },
      );
    }
  );
}

sub _convos_message {
  my($self, $args, $input, $response) = @_;
  my $login = $self->session('login');

  $self->send_partial(
    'event/message',
    highlight => 0,
    message => $input,
    nick =>  $login,
    server => $args->{server},
    status => 200,
    target => '',
    timestamp => time,
    uuid => $args->{uuid}.'_',
  );
  $self->send_partial(
    'event/message',
    highlight => 0,
    message => $response,
    nick => $args->{server},
    server => $args->{server},
    status => 200,
    target => '',
    timestamp => time,
    uuid => $args->{uuid},
  );
}

sub _handle_socket_data {
  my ($self, $dom) = @_;
  my $cmd = Mojo::Util::html_unescape($dom->text(0));
  my $login = $self->session('login');

  if($cmd =~ s!^/(\w+)\s*(.*)!!) {
    my($action, $arg) = ($1, $2);
    $arg =~ s/\s+$//;
    if (my $code = Convos::Core::Commands->can($action)) {
      $cmd = $self->$code($arg, $dom);
    }
    else {
      return $self->_send_400($dom, 'Unknown command. Type /help to see available commands.');
    }
  }
  elsif($dom->{server} eq 'convos') {
    return $self->_convos_message($dom, $cmd, DEFAULT_RESPONSE);
  }
  elsif($dom->{target}) {
    $cmd = "PRIVMSG $dom->{target} :$cmd";
  }
  else {
    return;
  }

  if(defined $cmd) {
    my $key = "convos:user:$login:$dom->{server}";
    $cmd = "$dom->{uuid} $cmd";
    $self->logf(debug => '[%s] < %s', $key, $cmd);
    $self->redis->publish($key => $cmd);
    if($dom->{'data-history'}) {
      $self->redis->rpush("user:$login:cmd_history", $dom->text(0));
      $self->redis->ltrim("user:$login:cmd_history", -30, -1);
    }
  }
}

sub _send_400 {
  my($self, $args, $message) = @_;

  $self->send_partial(
    'event/server_message',
    status => 400,
    timestamp => time,
    uuid => '',
    server => $args->{'data-server'} || 'any',
    message => $message,
  );
}

=head1 COPYRIGHT

See L<Convos>.

=head1 AUTHOR

Jan Henning Thorsen

Marcus Ramberg

=cut

1;