package Pcore::App::Controller::ExtJS::REST;

use Pcore -role, -const, -status;
use Pcore::Util::Data qw[to_json from_json from_uri_query];

const our $FILTER_OPERATOR => {
    '<'    => '<',
    '<='   => '<=',
    '='    => '=',
    '>'    => '>',
    '>='   => '>=',
    '!='   => '!=',
    'LIKE' => 'LIKE',

    # 'NOTIN' => 'NOT IN',
    # 'IN'    => 'IN',
};

const our $SORT_DIRECTION => {
    ASC  => 'ASC',
    DESC => 'DESC',
};

around run => sub ( $orig, $self, $req ) {
    state $run = sub ( $self, $req, $method, $data ) {
        my $sub = $self->can("api_$method");

        # method is not implemented
        if ( !$sub ) {
            my $res = status [ 400, 'Method is not implemented' ];

            $req->( $res, [ 'Content-Type' => 'application/json' ], to_json $res )->finish;
        }

        # method found
        else {

            # authenticate
            $req->authenticate(
                sub ( $auth ) {

                    # authentication error
                    if ( !$auth ) {
                        $req->( $auth, [ 'Content-Type' => 'application/json' ], to_json $auth)->finish;
                    }

                    # authenticated
                    else {

                        # call rest method
                        $sub->(
                            $self, $req, $data,
                            sub ($res) {
                                $req->( $res, [ 'Content-Type' => 'application/json' ], to_json $res)->finish;

                                return;
                            }
                        );
                    }
                }
            );
        }

        return;
    };

    # CREATE
    if ( $req->env->{REQUEST_METHOD} eq 'POST' ) {

        # decode body
        my $data = from_json $req->body;

        $run->( $self, $req, 'create', $data );
    }

    # READ
    elsif ( $req->env->{REQUEST_METHOD} eq 'GET' ) {

        # decode params
        my $data = $req->env->{QUERY_STRING} ? from_uri_query $req->env->{QUERY_STRING} : {};

        if ( !defined $data->{id} ) {

            # decode filter
            if ( $data->{filter} ) {
                $data->{filter} = [ from_json $data->{filter} ];

                # validate filters operator
                for my $filter ( $data->{filter}->@* ) {
                    $filter->{operator} = $FILTER_OPERATOR->{ uc $filter->{operator} };

                    if ( !$filter->{operator} ) {
                        my $res = status [ 400, 'Invalid filter operator' ];

                        $req->( $res, [ 'Content-Type' => 'application/json' ], to_json $res )->finish;

                        return;
                    }
                }
            }

            # decode sort
            if ( $data->{sort} ) {
                $data->{sort} = [ from_json $data->{sort} ];

                # validate sort direction
                for my $sort ( $data->{sort}->@* ) {
                    $sort->{direction} = $SORT_DIRECTION->{ uc $sort->{direction} };

                    if ( !$sort->{direction} ) {
                        my $res = status [ 400, 'Invalid sort direction' ];

                        $req->( $res, [ 'Content-Type' => 'application/json' ], to_json $res )->finish;

                        return;
                    }
                }
            }
        }

        $run->( $self, $req, 'retrieve', $data );
    }

    # UPDATE
    elsif ( $req->env->{REQUEST_METHOD} eq 'PUT' ) {

        # decode body
        my $data = from_json $req->body;

        $run->( $self, $req, 'update', $data );
    }

    # DELETE
    elsif ( $req->env->{REQUEST_METHOD} eq 'DELETE' ) {

        # decode body
        my $data = from_json $req->body;

        $run->( $self, $req, 'delete', $data );
    }

    # bad request method, fallback to original method
    else {
        $self->$orig($req);
    }

    return;
};

1;
## -----SOURCE FILTER LOG BEGIN-----
##
## PerlCritic profile "pcore-script" policy violations:
## +------+----------------------+----------------------------------------------------------------------------------------------------------------+
## | Sev. | Lines                | Policy                                                                                                         |
## |======+======================+================================================================================================================|
## |    2 | 9, 12                | ValuesAndExpressions::ProhibitNoisyQuotes - Quotes used with a noisy string                                    |
## +------+----------------------+----------------------------------------------------------------------------------------------------------------+
##
## -----SOURCE FILTER LOG END-----
__END__
=pod

=encoding utf8

=head1 NAME

Pcore::App::Controller::ExtJS::REST

=head1 SYNOPSIS

=head1 DESCRIPTION

=head1 ATTRIBUTES

=head1 METHODS

=head1 SEE ALSO

=cut
