package Pcore::App::Selenium;

use Pcore qw[-cli -class];

extends qw[Pcore::App::Alien];

has '+name' => ( default => 'selenium' );
has '+ns'   => ( default => 'Pcore::Selenium' );

has selenium_ver => ( is => 'lazy', isa => Str, init_arg => undef );
has selenium_uri => ( is => 'lazy', isa => Str, init_arg => undef );
has ff_ver       => ( is => 'lazy', isa => Str, init_arg => undef );

has ff_dir      => ( is => 'lazy', isa => Str, init_arg => undef );
has ff_bin_path => ( is => 'lazy', isa => Str, init_arg => undef );

our $CFG = {
    SELENIUM => {
        SELENIUM_VER    => undef,
        FF_VER          => undef,
        PORT            => 4444,
        LINUX_XDISPLAY  => 20,
        USER_EXTENSIONS => q[],
        FF_EXTENSIONS   => {
            firebug    => 'https://addons.mozilla.org/firefox/downloads/latest/1843/addon-1843-latest.xpi',
            firexpath  => 'https://addons.mozilla.org/firefox/downloads/latest/11900/addon-11900-latest.xpi',
            noscript   => 'https://addons.mozilla.org/firefox/downloads/latest/722/addon-722-latest.xpi',
            flashblock => 'https://addons.mozilla.org/firefox/downloads/latest/433/addon-433-latest.xpi',

            # adblock    => 'https://addons.mozilla.org/firefox/downloads/latest/1865/addon-1865-latest.xpi',
        }
    }
};

sub _build_selenium_ver {
    my $self = shift;

    return $self->cfg->{SELENIUM}->{SELENIUM_VER} // $DIST->{SELENIUM}->{SELENIUM_VER};
}

sub _build_selenium_uri {
    my $self = shift;

    my $major_ver = $self->selenium_ver =~ s/\A(\d+[.]\d+)[.].*/$1/smr;

    return qq[http://selenium-release.storage.googleapis.com/$major_ver/selenium-server-standalone-] . $self->selenium_ver . q[.jar];
}

sub _build_ff_ver {
    my $self = shift;

    return $self->cfg->{SELENIUM}->{FF_VER} // $DIST->{SELENIUM}->{FF_VER};
}

sub _build_alien_bin_path {
    my $self = shift;

    return $self->app_dir . 'selenium-server-standalone-' . $self->selenium_ver . q[.jar];
}

sub _build_ff_dir {
    my $self = shift;

    return $self->app_dir . 'firefox-' . ( $MSWIN ? 'win' : 'nix' ) . q[-] . $self->ff_ver;
}

sub _build_ff_bin_path {
    my $self = shift;

    return $self->ff_dir . ( $MSWIN ? '/firefox.exe' : '/firefox-bin' );
}

sub _build_selenium {
    my $self = shift;

    if ( -f $self->alien_bin_path ) {
        $self->_appx_report_warn( q["] . $self->alien_bin_path . q[" already exists. Remove it manually to rebuild] );
    }
    else {
        # NOTE on linux don't forget to install dependencies: yum install Xvfb xorg-x11-fonts*
        $self->_appx_report_info( q[Downloading: selenium-server-standalone-] . $self->selenium_ver . q[.jar] );

        P->ua->mirror( $self->selenium_uri, $self->alien_bin_path, blocking => 1, on_progress => 1 );

        $self->_appx_report_fatal(q[Selenium deployment error]) if !-e $self->alien_bin_path;
    }

    return;
}

sub _build_ff {
    my $self = shift;

    if ( -d $self->ff_dir ) {
        $self->_appx_report_warn( q["] . $self->ff_dir . q[" already exists. Remove it manually to rebuild] );
    }
    else {
        my $uri = q[http://ftp.mozilla.org/pub/mozilla.org/firefox/releases/] . $self->ff_ver . ( $MSWIN ? q[/win32] : q[/linux-x86_64] ) . q[/en-US/];
        $uri .= $MSWIN ? q[Firefox%20Setup%20] . $self->ff_ver . q[.exe] : q[firefox-] . $self->ff_ver . q[.tar.bz2];
        my $store_to = $MSWIN ? $PROC->{TEMP_DIR} . 'firefox-install.exe' : $PROC->{TEMP_DIR} . 'firefox-install.tar.bz2';

        $self->_appx_report_info( q[Downloading: ] . $uri );

        P->ua->mirror( $uri, $store_to, blocking => 1, on_progress => 1 );

        P->file->mkpath( $self->ff_dir );

        if ($MSWIN) {
            P->capture->sys( qq[winrar x -o+ -apcore -df -ibck "$store_to" core "] . $self->ff_dir . q["] );
        }
        else {
            P->capture->sys( qq[tar --overwrite --strip-components=1 -xvf "$store_to" -C "] . $self->ff_dir . q["] );
        }

        if ( !-e $self->ff_bin_path ) {
            P->file->rmtree( $self->ff_dir );
            $self->_appx_report_fatal(q[Firefox deployment error]);
        }

        $self->_build_ff_extensions;
    }

    return;
}

sub _build_ff_extensions {
    my $self = shift;

    require Archive::Zip;

    for my $ext ( sort keys %{ $self->cfg->{SELENIUM}->{FF_EXTENSIONS} } ) {
        $self->_appx_report_info(qq[Downloading: $ext]);

        my $tempfile = P->file->tempfile( binmode => ':raw' );

        P->ua->mirror( $self->cfg->{SELENIUM}->{FF_EXTENSIONS}->{$ext}, $tempfile, blocking => 1, on_progress => 1 );

        my $zip = Archive::Zip->new;
        my $res = $zip->readFromFileHandle( $tempfile, $tempfile->filename );
        die q[Error reading .zip archive] unless $res == Archive::Zip::AZ_OK();

        my $id = P->data->from_xml( $zip->contents('install.rdf') =~ s/0$//smr )->{RDF}->{Description}->{'em:id'};

        P->file->copy( $tempfile->filename, $self->ff_dir . qq[/browser/extensions/$id.xpi] );
    }

    return;
}

sub _cmd {
    my $self = shift;

    # timeout - Controls how long the client is is allowed to be gone before the session is reclaimed (value in seconds)
    # browserTimeout -Controls how long the browser is allowed to hang (value in seconds)
    # Please note that the "browserTimeout" is intended as a backup timeout mechanism when the ordinary timeout mechanism fails, which should be used mostly in grid/server environments to ensure that crashed/lost processes do not stay around for too long, polluting the runtime environment.

    my $cmd = q[java -Xrs -jar "] . $self->alien_bin_path . q["];
    $cmd .= q[ -port ] . $self->cfg->{SELENIUM}->{PORT};
    $cmd .= q[ -timeout ] . ( 5 * 60 );
    $cmd .= q[ -browserTimeout ] . ( 6 * 60 );
    $cmd .= q[ -userExtensions ] . $self->cfg->{SELENIUM}->{USER_EXTENSIONS} if $self->cfg->{SELENIUM}->{USER_EXTENSIONS};    # indicates a JavaScript file that will beloaded into selenium
    $cmd .= q[ -ensureCleanSession];
    $cmd .= q[ -trustAllSSLCertificates];
    $cmd .= q[ -proxyInjectionMode];
    $cmd .= q[ -Dwebdriver.firefox.bin="] . $self->ff_bin_path . q["];
    $cmd .= q[ -Dwebdriver.accept.untrusted.certs=true];
    $cmd .= q[ -Dwebdriver.assume.untrusted.issuer=true];
    $cmd .= q[ -Dwebdriver.enable.native.events=true];

    return $cmd;
}

# APP
around _build_cfg => sub {
    my $orig = shift;
    my $self = shift;

    return P->hash->merge( $self->$orig, $CFG );
};

around _create_local_cfg => sub {
    my $orig = shift;
    my $self = shift;

    my $local_cfg = {
        SELENIUM => {
            SELENIUM_VER => $self->cfg->{SELENIUM}->{SELENIUM_VER},
            FF_VER       => $self->cfg->{SELENIUM}->{FF_VER},
            PORT         => $self->cfg->{SELENIUM}->{PORT},
        }
    };

    return P->hash->merge( $self->$orig, $local_cfg );
};

around app_build => sub {
    my $orig = shift;
    my $self = shift;

    $self->$orig;

    $self->_build_selenium;
    $self->_build_ff;

    return;
};

sub master_proc {
    my $self = shift;

    return;
}

sub alien_proc {
    my $self = shift;

    if ($MSWIN) {
        P->sys->system( $self->_cmd() );

        exit 0;
    }
    else {
        exec q[xvfb-run -n ] . $self->cfg->{SELENIUM}->{LINUX_XDISPLAY} . q[ -l -s "-fp /usr/share/X11/fonts/misc -screen 0 1024x768x24" ] . $self->_cmd() or die;
    }

    return;
}

1;
__END__
=pod

=encoding utf8

=cut
