package CatalystX::SimpleLogin::Form::Login;
use HTML::FormHandler::Moose;
use Try::Tiny;
use namespace::autoclean;

extends 'HTML::FormHandler';
use MooseX::Types::Moose qw/ HashRef /;
use MooseX::Types::Common::String qw/ NonEmptySimpleStr /;

has '+name' => ( default => 'login_form' );

has authenticate_args => (
    is        => 'ro',
    isa       => HashRef,
    predicate => 'has_authenticate_args',
);

has authenticate_realm => (
    is        => 'ro',
    isa       => NonEmptySimpleStr,
    predicate => 'has_authenticate_realm',
);

has 'login_error_message' => (
    is => 'ro',
    isa => NonEmptySimpleStr,
    required => 1,
    default => 'Wrong username or password',
);

foreach my $type (qw/ username password /) {
    has sprintf("authenticate_%s_field_name", $type) => (
        is => 'ro',
        isa => NonEmptySimpleStr,
        default => $type
    );
    # FIXME - be able to change field names in rendered form also!
}

has_field 'username' => ( type => 'Text', tabindex => 1 );
has_field 'password' => ( type => 'Password', tabindex => 2 );
has_field 'remember' => ( type => 'Checkbox', tabindex => 3 );
has_field 'submit'   => ( type => 'Submit', value => 'Login', tabindex => 4 );

sub validate {
    my $self = shift;

    # as HTML::Formhandler doesn't handle exceptions thrown by user provided
    # validate methods and fails to clear the 'posted' attribute we need to
    # catch them
    unless (
        try {
            $self->ctx->authenticate(
            {
                (map {
                    my $param_name = sprintf("authenticate_%s_field_name", $_);
                    ($self->can($param_name) ? $self->$param_name() : $_) => $self->values->{$_};
                }
                grep { ! /remember/ }
                keys %{ $self->values }),
                ($self->has_authenticate_args ? %{ $self->authenticate_args } : ()),
            },
            ($self->has_authenticate_realm ? $self->authenticate_realm : ()),
            );
        }
        catch {
            $self->ctx->log->error("$_");
            return 0;
        }
    ) {
        $self->add_auth_errors;
        # the return value of this method is ignored by HTML::FormHandler
        # 0.40064, only errors added to the form itself or its fields control
        # the forms' 'validated' attribute
        return 0;
    }
    return 1;
}

sub add_auth_errors {
    my $self = shift;
    $self->field( 'password' )->add_error( $self->login_error_message );
}

__PACKAGE__->meta->make_immutable;

=head1 NAME

CatalystX::SimpleLogin::Form::Login - validation for the login form

=head1 DESCRIPTION

A L<HTML::FormHandler> form for the login form.

=head1 FIELDS


=over

=item username

=item password

=item remember

=item submit

=back

=head1 METHODS

=over

=item validate

=item add_auth_errors

=back

=head1 SEE ALSO

=over

=item L<CatalystX::SimpleLogin::Controller::Login>

=back

=head1 CUSTOMIZATION

By default, the params passed to authenticate() are 'username' and
'password'. If you need to use different names, then you'll need to
set the correct value(s) via login_form_args in the configuration.
The keys are 'authenticate_username_field_name' and/or
'authenticate_password_field_name'.

    __PACKAGE__->config(
        'Controller::Login' => {
            login_form_args => {
               authenticate_username_field_name => 'name',
               authenticate_password_field_name => 'password2',
            },
        },
    );

You can also change the way that the form is displayed by setting
attributes.  In MyApp.pm:

    __PACKAGE__->config(
        'Controller::Login' => {
            login_form_args => {
               login_error_message => 'Login failed',
               field_list => [
                   '+submit' => { value => 'Login' },
               ]
            }
        },
    );

Additional fields can be added:

   field_list => [
       'foo' => ( type => 'MyField' ),
       'bar' => { type => 'Text' },
   ]

Additional arguments to the authenticate call can be added:
If your user table has a column C<status> and you want only those with C<status = 'active'>to be able to log .in

    __PACKAGE__->config(
        'Controller::Login' => {
            login_form_args => { 
                authenticate_args => { status => 1 },
            },
        },
    };

=head1 AUTHORS

See L<CatalystX::SimpleLogin> for authors.

=head1 LICENSE

See L<CatalystX::SimpleLogin> for license.

=cut