package CF::Helpers::Core;

use base 'Mojolicious::Plugin';
use feature 'signatures';
no warnings qw{ experimental::signatures } ;

use YAML;
use Mojo::JWT;
use Mojo::JSON qw(encode_json);

sub register ($class, $self, $conf) {

    $self->helper(error => sub ($c, $status, $msg) {

        if ( ref $msg eq 'ARRAY' ) {
            $errors = $msg;
        }
        elsif ( ref $msg eq 'HASH' ) {
            $errors = [ $msg ];
        }
        else {
            $errors = [{
                message => $msg,
                path    => $c->stash('openapi.path'),
            }];
        }

        $c->stash(
            status  => $status,
            openapi => {
                status  => $status,
                errors => $errors,
            },
        );

        return undef;
    });

    $self->helper( trace => sub( $c, $data='' ) {
        $data = Dump $data if ref $data;
        $c->app->log->debug($data);

    });

    $self->helper( spec_filter => sub ($c, $data, $class) {

        if (my $def =$c->openapi->spec("/components/schemas/$class")) {
            my $filtered = {};

            KEY:
            foreach my $key ( keys %{ $def->{properties} } ) {
                my $value    = $data->{$key};
                my $nullable = 0;

                my $types = $def->{properties}{$key}{type};

                if ( ref $types eq 'ARRAY' ) {
                    TYPE:
                    foreach my $type ( @{ $types } ) {
                        $nullable = 1, last if $type eq 'null';
                    }
                }

                $filtered->{$key} = $value;
            }

            $data = $filtered;

        }

        return $data;
    });

    $self->helper( search_parametrs => sub( $c, $args ) {
        my $conditions = {};
        if ( ref $args->{filter} eq 'ARRAY' ) {
            CONDITION:
            foreach my $condition ( @{ $args->{filter} } ) {
                my ( $column, $op, $arg ) = split /:/, $condition;
                my @columns = split /\|/, $column;
                my @args    = split /\|/, $arg;

                if ( $op eq 'match' ) {
                    $op  = 'ilike';
                    @args = map { '%' . $_ . '%' } @args;
                }
                elsif ( $op =~ /^beg/i ) {
                    $op  = 'ilike';
                    @args = map { $_ . '%' } @args;
                }
                elsif ( $op =~ /^end/i ) {
                    $op  = 'ilike';
                    @args = map { '%' . $_ } @args;
                }
                else {
                    if ( scalar @args > 1 ) {
                        $op = 'in';
                    }
                    else {
                        $op = '=';
                    }
                }

                if ( scalar @columns > 1 ) {
                    $conditions->{ -or } = [
                        map { $_ => { $op => \@args }} @columns
                    ];
                }
                else {
                    $conditions->{ $column } = { $op => \@args };
                }
            }
        }

        if ( $args->{fulltext} ) {
            $conditions->{fulltext} = { ilike => '%' . $args->{fulltext} . '%'};
        }

        my @order_by = ();
        if ( ref $args->{sort} eq 'ARRAY' ) {
            COLUMN:
            foreach my $column ( @{ $args->{sort} } ) {
                $column =~ s/^(\W)*//;

                if ( $1 && $1 eq '-' ) {
                    push @order_by, { -desc => $column };
                }
                else {
                    push @order_by, $column;
                }
            }
        }

        return (
            $conditions,
            {
                order_by => \@order_by,
#                columns  => $args->{columns},
                rows     => $args->{limit},
                offset   => $args->{offset},
            }
        );
    });

    $self->helper( prepare_update_data => sub($c, $result, $args) {
        my $update = {};

        my %protected = ();
        if ( $result->resultset_class->can('PROTECTED_FIELDS')) {
            %protected = map {$_ => 1}
                @{ $result->resultset_class->PROTECTED_FIELDS()};
        }

        FIELD:
        foreach my $field ( $result->columns() ) {
            if ( exists $args->{$field} && ! $protected{$field} ) {
                $update->{$field} = $args->{$field} ;
            }
        }

        return $update;
    });

    $self->helper( format_timestamp => sub($c, $timestamp, $format) {
        return $timestamp;
    });

    $self->helper( jitsi_url => sub ( $c, $room, $lifetime=180, %user ) {

        $user{avatar} //= join ('',
            $c->cfg->{piratar_base_url},
            $user{username},
           '.jpg',
        );

        my $jwt = Mojo::JWT->new(
            secret => $c->cfg->{jitsi_token_secret},
            claims => {
                aud  => 'jitsi',
                iss  => 'cf-online',
                sub  => 'meet.jitsi',
                room => $room,
                moderator => $user{moderator} ? \1:\0,
                exp  => time + $lifetime,
                context => {
                    user   => {
                        avatar => $user{avatar},
                        name   => $user{name},
                        email  => $user{mail},
                    }
                },
            }
        )->encode;

        return join ('',
            $c->cfg->{jitsi_base_url},
            $c->cfg->{jitsi_room},
            '?jwt=',
            $jwt,
        );

    });

    $self->helper( notify => sub ( $c, $event, $payload ) {

        my $content = {
            event   => $event,
            payload => $payload,
        };

        if  ($self->cfg->{message_broker} eq 'redis' ) {
            $self->redis->pubsub->notify( notify => encode_json($content) );
        }
        else {
            my $pubsub = Mojo::Pg::PubSub->new(pg => $self->pg);
            $pubsub->json('notify')->notify( notify => $content );
        }

    });
}

1;