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

use Pcore -role;
use Pcore::Util::Data qw[to_json];
use Pcore::ExtJS;

with qw[Pcore::App::Controller];

requires qw[_build_ext_map];

has api_ver                => ( is => 'ro',   isa => Str );                          # API version
has ext_map                => ( is => 'lazy', isa => HashRef, init_arg => undef );
has perl_ext_app_namespace => ( is => 'ro',   isa => Str, init_arg => undef );
has ext_app_namespace      => ( is => 'ro',   isa => Str, init_arg => undef );
has ext_namespace          => ( is => 'ro',   isa => Str, init_arg => undef );

# this method can be overrided in the child class
sub BUILD ( $self, $args ) {
    return;
}

# this method can be overrided in the child class
sub run ( $self, $req ) {

    if ( $req->{path_tail} && $req->{path_tail}->is_file ) {

        # try to return static content
        $self->return_static($req);
    }
    else {
        $req->(404)->finish;
    }

    return;
}

around BUILD => sub ( $orig, $self, $args ) {
    $self->$orig($args);

    my $app_namespace = ref $self->{app};

    # this is ExtJS application class
    if ( $self->does('Pcore::App::Controller::ExtJS::App') ) {
        $self->{perl_ext_app_namespace} = ref $self;

        $self->{ext_app_namespace} = ref($self) =~ s/\A$app_namespace\::Index::/$app_namespace\::/smr;

        $self->{ext_app_namespace} =~ s/:://smg;

        $self->{ext_namespace} = $self->{ext_app_namespace};

        $self->{api_ver} //= 'v1';
    }

    # this is ExtJS namespace class
    else {
        # find ExtJS app instance
        my $ext_app;

        my $parent_class = ref $self;

        while (1) {
            last unless $parent_class =~ s/::[^:]+\z//sm;

            last if $parent_class eq $app_namespace;

            if ( eval { $parent_class->does('Pcore::App::Controller::ExtJS::App') } ) {
                $ext_app = $self->{app}->{router}->get_instance($parent_class);

                last;
            }
        }

        die q[ExtJS app wasn't found] if !$ext_app;

        $self->{perl_ext_app_namespace} = ref $ext_app;

        $self->{ext_app_namespace} = $ext_app->{ext_namespace};

        my ( $path, $ext_app_path ) = ( $self->path, $ext_app->path );

        $self->{ext_namespace} = $path =~ s/$ext_app_path/$self->{ext_app_namespace}./smr;

        # remove last "/"
        substr $self->{ext_namespace}, -1, 1, q[];

        $self->{ext_namespace} =~ s[/][.]smg;

        $self->{api_ver} //= $ext_app->{api_ver};
    }

    # validate ExtJS class
    die qq[ExtJS namespace "$self->{ext_namespace}" is invalid] if $self->{ext_namespace} =~ /[^A-Za-z0-9.]/smg;

    # check ext_map
    my $ext_map = $self->ext_map;

    for my $class ( keys $ext_map->{class}->%* ) {
        die qq["$class" must be lowercase alpha name] if $class !~ /\A[[:lower:]]+\z/sm;

        die qq["ext_class_$class" method is required to generate ExtJS class] if !$self->can("ext_class_$class");

        # find real base class name
        my $real_base_class_name = $Pcore::ExtJS::BOOTSTRAP->{classic}->{class}->{ $ext_map->{class}->{$class} };

        die qq[ExjJS class "$ext_map->{class}->{$class}" is not registered] if !$real_base_class_name;

        my $class_name = ucfirst lc $class;

        my $perl_class_name = ref($self) . "::$class_name";

        my $ext_class_name = "$self->{ext_namespace}.$class_name";

        # register perl class
        $Pcore::ExtJS::BOOTSTRAP->{classic}->{class}->{$perl_class_name} = $ext_class_name;

        # get base class alias
        if ( my $base_class_alias = $Pcore::ExtJS::BOOTSTRAP->{classic}->{class_to_alias}->{$real_base_class_name} ) {
            my ($alias_namespace) = ( $base_class_alias =~ /(.+)[.][^.]+\z/sm );

            # generate alias
            my $alias = "$alias_namespace." . ( lc $ext_class_name =~ s/[.]/-/smgr );

            # register ExtJS class alias
            $Pcore::ExtJS::BOOTSTRAP->{classic}->{class_to_alias}->{$ext_class_name} = $alias;

            $Pcore::ExtJS::BOOTSTRAP->{classic}->{alias_to_class}->{$alias} = $ext_class_name;
        }
    }

    return;
};

around run => sub ( $orig, $self, $req ) {

    # .js file request
    if ( $req->{path_tail} && $req->{path_tail} =~ /\A(.+)[.]js\z/sm ) {
        my $extjs_class_name = $1;

        my $internal_class_name = lc $extjs_class_name;

        if ( my $extend = $self->{ext_map}->{class}->{$internal_class_name} ) {
            my $readable = 1;

            # create ExtJS class generation context
            my $ext = bless {
                app        => $self->{app},
                req        => $req,
                readable   => $readable,
                namespace  => $self,
                class_name => $extjs_class_name,
                extend     => $extend,
                cb         => sub ( $ext, $cfg ) {
                    $ext->{cfg} = $cfg;

                    # generate class
                    $req->( 200, [ 'Content-Type' => 'application/javascript' ], $ext->to_js )->finish;

                    return;
                },
              },
              'Pcore::ExtJS';

            my $method = "ext_class_$internal_class_name";

            # call ExtJS class generation method
            $self->$method($ext);

            return;
        }
    }

    # fall back to the original method
    $self->$orig($req);

    return;
};

1;
## -----SOURCE FILTER LOG BEGIN-----
##
## PerlCritic profile "pcore-script" policy violations:
## +------+----------------------+----------------------------------------------------------------------------------------------------------------+
## | Sev. | Lines                | Policy                                                                                                         |
## |======+======================+================================================================================================================|
## |    1 | 93                   | RegularExpressions::ProhibitEnumeratedClasses - Use named character classes ([^A-Za-z] vs. [[:^alpha:]])       |
## +------+----------------------+----------------------------------------------------------------------------------------------------------------+
##
## -----SOURCE FILTER LOG END-----
__END__
=pod

=encoding utf8

=head1 NAME

Pcore::App::Controller::ExtJS -  - ExtJS namespace HTTP controller

=head1 SYNOPSIS

=head1 DESCRIPTION

=head1 ATTRIBUTES

=head1 METHODS

=head1 SEE ALSO

=cut
