package Lemonldap::NG::Portal::Captcha::SecurityImage;

use strict;
use Mouse;
use MIME::Base64;
use GD::SecurityImage use_magick => 1;

our $VERSION = '2.0.15';

extends 'Lemonldap::NG::Portal::Main::Plugin';

has width => (
    is      => 'rw',
    lazy    => 1,
    default => sub { $_[0]->{conf}->{captchaWidth} || 220 }
);
has height => (
    is      => 'rw',
    lazy    => 1,
    default => sub { $_[0]->{conf}->{captchaHeight} || 60 }
);
has lines => (
    is      => 'rw',
    lazy    => 1,
    default => sub { $_[0]->{conf}->{captchaLines} || 4 }
);
has scramble => (
    is      => 'rw',
    lazy    => 1,
    default => sub { $_[0]->{conf}->{captchaScramble} || 1 }
);
has fgColor => (
    is      => 'rw',
    lazy    => 1,
    default => sub { $_[0]->{conf}->{captchaFg} || '#403030' }
);
has bgColor => (
    is      => 'rw',
    lazy    => 1,
    default => sub { $_[0]->{conf}->{captchaBg} || '#FF644B' }
);
has rndmax => (
    is      => 'rw',
    lazy    => 1,
    default => sub { $_[0]->{conf}->{captcha_size} || 6 }
);
has timeout => (
    is      => 'rw',
    lazy    => 1,
    default => sub { $_[0]->{conf}->{formTimeout} }
);

has ott => (
    is      => 'rw',
    lazy    => 1,
    default => sub {
        my $ott = $_[0]->{p}->loadModule('::Lib::OneTimeToken');
        $ott->timeout( $_[0]->timeout );
        return $ott;
    }
);

sub init {
    my ($self) = @_;
    $self->addUnauthRoute( renewcaptcha => '_sendCaptcha', ['GET'] );
    return 1;
}

# Internal methods
sub _getCaptcha {
    my ($self) = @_;
    my $image = GD::SecurityImage->new(
        width    => $self->width,
        height   => $self->height,
        lines    => $self->lines,
        gd_font  => 'Giant',
        scramble => $self->scramble,
        rndmax   => $self->rndmax,
    );
    $image->random;
    $image->create( 'normal', 'default', $self->fgColor, $self->bgColor );
    my ( $imageData, $mimeType, $rdm ) = $image->out( force => 'png' );
    my $img   = 'data:image/png;base64,' . encode_base64( $imageData, '' );
    my $token = $self->ott->createToken( { captcha => $rdm } );
    return ( $token, $img );
}

sub _sendCaptcha {
    my ( $self, $req ) = @_;
    $self->logger->info("User request for captcha renew");
    my ( $token, $image ) = $self->_getCaptcha($req);

    return $self->p->sendJSONresponse( $req,
        { newtoken => $token, newimage => $image } );
}

sub _validate_captcha_token {
    my ( $self, $token, $value ) = @_;
    my $s = $self->ott->getToken($token);
    unless ($s) {
        $self->logger->warn("Captcha token $token isn't valid");
        return 0;
    }
    unless ( $s->{captcha} eq $value ) {
        $self->logger->notice('Bad captcha response');
        return 0;
    }
    $self->logger->debug('Good captcha response');
    return 1;
}

sub _get_captcha_html {
    my ( $self, $req, $src ) = @_;

    my $sp = $self->p->staticPrefix;
    $sp =~ s/\/*$/\//;

    return $self->loadTemplate(
        $req,
        'captcha',
        params => {
            STATIC_PREFIX => $sp,
            CAPTCHA_SRC   => $src,
            CAPTCHA_SIZE  => $self->rndmax,
        }
    );
}

# New API
sub check_captcha {
    my ( $self, $req ) = @_;
    my $token = $req->param('token');
    unless ($token) {
        $self->logger->warn("No token provided for Captcha::SecurityImage");
        return 0;
    }

    my $value = $req->param('captcha');
    unless ($value) {
        $self->logger->warn("No response provided for Captcha::SecurityImage");
        return 0;
    }

    return $self->_validate_captcha_token( $token, $value );
}

sub init_captcha {
    my ( $self, $req ) = @_;

    my ( $token, $image ) = $self->_getCaptcha;
    $self->logger->debug('Prepare captcha');
    $req->token($token);
    $req->captchaHtml( $self->_get_captcha_html( $req, $image ) );

    # DEPRECATED: Compatibility with old templates
    $req->captcha($image);
}

# #######
# Old API
# TODO: Remove this in 3.0
# #######

sub validateCaptcha {
    my ( $self, $token, $value ) = @_;
    return $self->_validate_captcha_token( $token, $value );
}

sub setCaptcha {
    my ( $self, $req ) = @_;
    $self->init_captcha($req);
}

sub sendCaptcha {
    my ( $self, $req ) = @_;
    return $self->_sendCaptcha($req);
}

sub getCaptcha {
    my ( $self, $req ) = @_;
    return $self->_getCaptcha($req);
}

1;
