package CF::Controller::Posts;

use Mojo::Base 'Mojolicious::Controller';
use Mojo::Pg::PubSub;

use feature 'signatures';
no warnings 'experimental::signatures';

# TODO: do modelu
use constant STATUS_ANNOUNCEMENTS => {
    1 => 2, # prijatelny n.p.
    2 => 1, # schvaleny n.p.
    4 => 0, # zamitnuty n.p.
};

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

    my $args   = $c->req->json;

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

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

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

    # limit poctu prispevku jedneho uzivatele k jednemu bodu
    my $limit = $c->schema->resultset('Post')->count({
        program_entry_id => $program_entry->id,
        user_id          => $c->user->{id},
    });

    if ( $limit > $c->cfg->{limit_post_count}) {
        return $c->error(429, 'Too many post from user');
    }

    # limit poctu prispevku za minutu
    $limit = $c->schema->resultset('Post')->count({
        user_id          => $c->user->{id},
        datetime         => { '>' => \"now()-'1 min'::interval" },
    });

    if ( $limit >= $c->cfg->{limit_post_add_rate}) {
        return $c->error(429, 'Too many posts per minute');
    }

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

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

    $pubsub->json('notify')->notify( notify => {
        event   => 'post_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 ( $c->user ) {
        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 );

    $cond->{deleted} = undef;

    # 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 aktivni bod rozpravy
        if ( ! $program_entry ) {
            $c->render(json => { data  => [], total => 0, });
            return
        }

        $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  = {};
    if ( $c->user ) {
        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 $post = $c->schema->resultset('Post')->find($c->stash->{id});
    return $c->error(404, 'Post not found') if ! $post;

    # limit poctu prispevku za minutu
    my $limit = $c->schema->resultset('PostHistory')->count({
        user_id  => $c->user->{id},
        post_id  => $post->id,
        datetime => { '>' => \"now()-'1 min'::interval" },
    });

    if ( $limit >= $c->cfg->{limit_post_edit_rate}) {
        return $c->error(429, 'Too many posts changes per minute');
    }

    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};
        }
    }

    my $update = $c->prepare_update_data( $post, $c->req->json );
    my $pubsub = Mojo::Pg::PubSub->new(pg => $c->pg);
    my $guard  = $c->schema->txn_scope_guard;

    if ( $update->{state} && $post->state != $update->{state} ) {
        my $announcement_type = STATUS_ANNOUNCEMENTS->{ $args->{state} };

        if ( $announcement_type ) {
            #TODO: do modelu
            my $msg = $c->schema->resultset('Announcement')->from_template(
                $announcement_type,
                $post->user->name,
                $post->content,
            );

            my $announcement = $c->schema->resultset('Announcement')->create({
                user_id  => $c->user->{id},
                type     => $announcement_type,
                content  => $msg,
            });

            # potrebujeme kvuli datumu
            $announcement = $c->schema->resultset('Announcement')->find({
                id => $announcement->id
            });

            $pubsub->json('notify')->notify( notify => {
                event   => 'announcement_created',
                payload => $announcement->format(),
            });
        }
    }

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

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

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

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

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

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

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

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

    $pubsub->json('notify')->notify( notify => {
        event   => 'post_deleted',
        payload => {
            id => $post->id,
        }
    });

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

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

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

    if ( $post->type == 0 and ! $c->user_roles->{member} ) {
        $c->render(status => 403, text => '');
        return;
    }

    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 $pubsub = Mojo::Pg::PubSub->new(pg => $c->pg);
    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('notify')->notify(notify => {
        event   => 'post_ranked',
        payload => {
            id => $post->id,
            %{ $update },
        }
    });

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





1;