package Catalyst::Plugin::Authentication::Store::DBIx::Class::User;

use strict;
use warnings;
use base qw/Catalyst::Plugin::Authentication::User/;
use base qw/Class::Accessor::Fast/;

BEGIN {
    __PACKAGE__->mk_accessors(qw/config resultset _user _roles/);
}

sub new {
    my ( $class, $config, $c) = @_;

    my $self = {
        resultset => $c->model($config->{'user_class'}),
        config => $config,
        _user => undef
    };
    
    bless $self, $class;
    

    
    ## if we have lazyloading turned on - we should not query the DB unless something gets read.
    ## that's the idea anyway - still have to work out how to manage that - so for now we always force
    ## lazyload to off.
    $self->config->{lazyload} = 0;
    
#    if (!$self->config->{lazyload}) {
#        return $self->load_user($authinfo, $c);
#    } else {
#        ## what do we do with a lazyload?
#        ## presumably this is coming out of session storage.  
#        ## use $authinfo to fill in the user in that case?
#    }

    return $self;
}


sub load {
    my ($self, $authinfo, $c) = @_;
    
    my $dbix_class_config = 0;
    
    if (exists($authinfo->{'dbix_class'})) {
        $authinfo = $authinfo->{'dbix_class'};
        $dbix_class_config = 1;
    }
    
    ## User can provide an arrayref containing the arguments to search on the user class.
    ## or even provide a prepared resultset, allowing maximum flexibility for user retreival.
    ## these options are only available when using the dbix_class authinfo hash. 
    if ($dbix_class_config && exists($authinfo->{'resultset'})) {
        $self->_user($authinfo->{'resultset'}->first);
    } elsif ($dbix_class_config && exists($authinfo->{'searchargs'})) {
        $self->_user($self->resultset->search(@{$authinfo->{'searchargs'}})->first);    
    } else {
        ## merge the ignore fields array into a hash - so we can do an easy check while building the query
        my %ignorefields = map { $_ => 1} @{$self->config->{'ignore_fields_in_find'}};                                    
        my $searchargs = {};
        
        # now we walk all the fields passed in, and build up a search hash.
        foreach my $key (grep {!$ignorefields{$_}} keys %{$authinfo}) {
            if ($self->resultset->result_source->has_column($key)) {
                $searchargs->{$key} = $authinfo->{$key};
            }
        }
        $self->_user($self->resultset->search($searchargs)->first);
    }

    if ($self->get_object) {
        return $self
    } else {
        return undef;
    }
    #$c->log->debug(dumper($self->{'user'}));

}

sub supported_features {
    my $self = shift;

    return {
        session         => 1,
        roles           => 1,
    };
}


sub roles {
    my ( $self ) = shift;
    ## this used to load @wantedroles - but that doesn't seem to be used by the roles plugin, so I dropped it.

    ## shortcut if we have already retrieved them
    if (ref $self->_roles eq 'ARRAY') {
        return(@{$self->_roles});
    }
    
    my @roles = ();
    if (exists($self->config->{'role_column'})) {
        @roles = split /[ ,\|]/, $self->get($self->config->{'role_column'});
        $self->_roles = \@roles;
    } elsif (exists($self->config->{'role_relation'})) {
        my $relation = $self->config->{'role_relation'};
        if ($self->_user->$relation->result_source->has_column($self->config->{'role_field'})) {
            @roles = $self->_user->$relation->search(undef, { columns => [ $self->config->{'role_field'}]})->all();
        } else {
            Catalyst::Exception->throw("role table does not have a column called " . $self->config->{'role_field'});
        }
        my $rolefield = $self->config->{'role_field'};
        @{$self->_roles} =  map { $_->get_column($self->config->{'role_field'}) } @roles;
    } else {
        Catalyst::Exception->throw("user->roles accessed, but no role configuration found");
    }

    return @{$self->_roles};
}

sub for_session {
    my $self = shift;
    
    return $self->get('id');
}

sub from_session {
    my ($self, $frozenuser, $c) = @_;
    
    # this could be a lot better.  But for now it just assumes $frozenuser is an id and uses find_user
    # XXX: hits the database on every request?  Not good...
    return $self->load( { id => $frozenuser }, $c);
}

sub get {
    my ($self, $field) = @_;
    
    if ($self->_user->can($field)) {
        return $self->_user->$field;
    } else {
        return undef;
    }
}

sub obj {
    my $self = shift;
    
    return $self->get_object;
}

sub get_object {
    my $self = shift;
    
    return $self->_user;
}

sub AUTOLOAD {
    my $self = shift;
    (my $method) = (our $AUTOLOAD =~ /([^:]+)$/);
    return if $method eq "DESTROY";

    $self->_user->$method(@_);
}

1;
__END__

=head1 NAME

Catalyst::Plugin::Authentication::Store::DBIx::Class::User - A class to ...

=head1 VERSION

This documentation refers to version 0.01.

=head1 SYNOPSIS

use Catalyst::Plugin::Authentication::Store::DBIx::Class::User;

=head1 DESCRIPTION

The Catalyst::Plugin::Authentication::Store::DBIx::Class::User class implements ...

=head1 SUBROUTINES / METHODS

=head2 new (constructor)

Parameters:
    class
    authinfo
    config
    c
    lazyload

Insert description of constructor here...

=head2 load_user (method)

Parameters:
    authinfo
    c

Insert description of method here...

=head2 supported_features (method)

Parameters:
    none

Insert description of method here...

=head2 roles

Parameters:
    none

Insert description of subroutine here...

=head2 for_session

Parameters:
    none

Insert description of subroutine here...

=head2 get (method)

Parameters:
    field

Insert description of method here...

=head2 obj (method)

Parameters:
    none

Insert description of method here...

=head2 get_object (method)

Parameters:
    none

Insert description of method here...

=head2 AUTOLOAD (method)

Parameters:
    none

Insert description of method here...

=head1 DEPENDENCIES

Modules used, version dependencies, core yes/no

strict

warnings

=head1 NOTES

...

=head1 BUGS AND LIMITATIONS

None known currently, please email the author if you find any.

=head1 AUTHOR

Jason Kuri (jk@domain.tld)

=head1 LICENCE

Copyright 2006 by Jason Kuri.

This software is free.  It is licensed under the same terms as Perl itself.

=cut
