package CF::Controller::Posts;

use Mojo::Base 'Mojolicious::Controller';
use Mojo::Pg::PubSub;
use feature 'signatures';
no warnings qw{ experimental::signatures };

sub create ($c) {
    $c->openapi->valid_input or return;

    my $args   = $c->req->json;
    my $pubsub = Mojo::Pg::PubSub->new(pg => $c->pg);

    # Navrh postupu muze predlozit jenom clen
    if ( $args->{type} == 0 && ! $c->user_roles->{member} ) {
        return $c->error(401, 'Insufficient permissions');
    }

    my $program_entry = $c->schema->resultset('ProgramEntry')->search({
        is_live => 1,
    })->first;

    # Neni zadny aktivnibod rozpravy
    if ( ! $program_entry ) {
        return $c->error(403, 'Debate closed');
    }

    my $post = $program_entry->add_to_posts({
        user_id          => $c->user->{id},
        type             => $args->{type},
        content          => $args->{content},
    });

    $pubsub->json('posts')->notify( posts => {
        event   => 'created',
        payload => $post->view->format(),
    });

    $c->render(
        status  => 201,
        openapi => { id => $post->id },
    );
};

sub get ($c) {
    $c->openapi->valid_input or return;

    my $post = $c->schema->resultset('Post_view')->find($c->stash->{id});
    return $c->error(404, 'Post not found') if ! $post;

    my $formatted = $post->format();

    if (my $my_vote = $post->rankings({ user_id => $c->user->{id} })->first) {
        $formatted->{ranking}{my_vote} = $my_vote->ranking;
    }

    foreach my $history ( $post->history() ) {
        push @{ $formatted->{history_log} }, {
            attribute  => 'content',
            value      => $history->content,
            datetime   => $history->datetime,
            originator => '',
        }
    }

    $c->render(openapi => $c->spec_filter($formatted, 'Post'));
}

sub list ($c) {
    $c->openapi->valid_input or return;
    my $args = $c->validation->output;

    my ($cond, $attrs) = $c->search_parametrs( $args );

    # Vzdy jen k jednemu programovemu bodu - parametr nebo aktualni
    if ( $args->{program_entry_id} ) {
        $cond->{program_entry_id} = $args->{program_entry_id};
    }
    else {
        my $program_entry = $c->schema->resultset('ProgramEntry')->search({
            is_live => 1,
        })->first;

        # Neni zadny aktivnibod rozpravy
        if ( ! $program_entry ) {
            return $c->error(403, 'Debate closed');
        }

        $cond->{program_entry_id} = $program_entry->id;
    }

    if ( exists $args->{archived} && defined $args->{archived}) {
        $cond->{is_archived} = $args->{archived};
    }

    # omezeni zobrazovani nepotvrzenych navrhu postupu
    if ( $args->{type} ) {
        $cond->{type} = $args->{type};
        if ( $args->{type} == 0 && ! $c->user_roles->{chairman} ) {
            $cond->{state} = {'-in' => [1,2,3,4]}
        }
    }
    elsif (! $c->user_roles->{chairman}) {
        $cond->{'-or'} = {
            type  => 1,
            state => { '-in' => [1,2,3,4] },
        };
    }

    # hodnoceni aktualniho uzivatele
    my $my_vote  = {};

    my $rankings = $c->schema->resultset('PostRanking')->search({
        user_id => $c->user->{id}
    });
    while ( my $record = $rankings->next() ) {
        $my_vote->{ $record->post_id } = $record->ranking;
    }

    my @posts = ();

    my $count = $c->schema->resultset('Post_view')->count($cond);

    if ( $count ) {
        my $posts = $c->schema->resultset('Post_view')->search($cond, $attrs);

        POST:
        while ( my $post = $posts->next() ) {
            my $formatted = $post->format();
            $formatted->{ranking}{my_vote} = $my_vote->{ $post->id } // 0;
            push @posts, $c->spec_filter($formatted, 'Post');
        }
    }

    $c->render(json => {
        data  => \@posts,
        total => $count,
    });
}

sub update ($c) {
    $c->openapi->valid_input or return;

    my $args   = $c->req->json;
    my $pubsub = Mojo::Pg::PubSub->new(pg => $c->pg);
    my $update = {};

    my $post = $c->schema->resultset('Post')->find($c->stash->{id});
    return $c->error(404, 'Post not found') if ! $post;

    if ( ! $c->user_roles->{chairman} ) {
        if ( $post->user_id != $c->user->{id} ) {
            return $c->error(403, 'Access deined');
        }
        else {
            delete $args->{is_archived};
            delete $args->{state};
        }
    }

    foreach my $key (qw(is_archoved state content)) {
        $update->{$key} = $args->{$key} if exists $args->{$key};
    }

    my $guard = $c->schema->txn_scope_guard;

    $post->add_to_history({
        user_id  => $c->user->{id},
        datetime => $post->changed // $post->datetime,
        content  => $post->content,
    });

    $post->update({
        %{ $update },
        changed => \'now()',
    });

    $pubsub->json('posts')->notify( posts => {
        event   => 'changed',
        payload => {
            id => $post->id,
            %{ $update },
        }
    });

    $guard->commit;

    $c->render(status => 204, text => '');
}

sub ranking ($c) {
    $c->openapi->valid_input or return;
    my $args = $c->validation->output;
    my $pubsub = Mojo::Pg::PubSub->new(pg => $c->pg);

    my $post = $c->schema->resultset('Post')->find($c->stash->{id});
    return $c->error(404, 'Post not found') if ! $post;

    my $user_ranking = $post->rankings({
        user_id => $c->user->{id},
    })->first;

    my $update = {
        ranking_likes    => $post->ranking_likes,
        ranking_dislikes => $post->ranking_dislikes,
    };

    my $user_ranking_update;

    if ($user_ranking && $user_ranking->ranking == $args->{ranking} ) {
        $user_ranking_update = 0;
    }
    else {
        $user_ranking_update = $args->{ranking};
    }

    if ($user_ranking && $user_ranking->ranking == 1 ) {
        $update->{ranking_likes}--;
        $update->{ranking_dislikes}++ if $args->{ranking} == -1;
    }
    elsif ($user_ranking && $user_ranking->ranking == -1 ) {
        $update->{ranking_dislikes}--;
        $update->{ranking_likes}++ if $args->{ranking} == 1;
    }
    else {
        $update->{ranking_likes}++ if $args->{ranking} == 1;
        $update->{ranking_dislikes}++ if $args->{ranking} == -1;
    }

    my $guard = $c->schema->txn_scope_guard;

    $post->update( $update );

    if ( $user_ranking ) {
        $user_ranking->update( { ranking => $user_ranking_update } );
    }
    else {
        $post->add_to_rankings({
            user_id => $c->user->{id},
            ranking => $user_ranking_update,
        });
    }

    $pubsub->json('posts')->notify(posts => {
        event   => 'ranked',
        payload => {
            id => $post->id,
            %{ $update },
        }
    });
    $guard->commit;

    $c->render(status => 204, text => '');
}

sub ws {
    my $c = shift;

    $c->inactivity_timeout(600);

    my $pubsub = Mojo::Pg::PubSub->new(pg => $c->pg);

    $pubsub->listen(posts => sub($pubsub, $payload) {
        if (
            $payload->{type} == 0
            && $payload->{state} == 0
            && ! $c->user_roles->{chairman}
        ) {
            return;
        }
        $c->send($payload);
    });

    $c->on(finish => sub ($c, $code, $reason = undef) {
        $pubsub->unlisten('streams');
        $c->app->log->debug("WebSocket closed with status $code");
    });
}

1;