package LIMS::Model::User;

use base 'LIMS::Model::Base';

use strict;
use Data::Dumper;

#-------------------------------------------------------------------------------
# uses $id to get user:
sub get_user_profile {
    my $self = shift;
    my $id   = shift;

    # load speculative so update_user_details() doesn't crash for 'new user':
    my $user = LIMS::DB::User->new(id => $id)->load(speculative => 1);

    return $user;
}

#-------------------------------------------------------------------------------
# uses one of unique keys to get user:
sub get_user_details {
    my $self = shift;
    my $args = shift;

    my %params = (
        with        => 'user_location',
        speculative => 1, # in case $args->{value} incorrect (eg email)
        use_key     => $args->{col}, # need to tell RDBO which key to use
    );
#$Rose::DB::Object::Debug = 1;
    my $user = LIMS::DB::User
        ->new( $args->{col} => $args->{value} )
        ->load( %params );
#$Rose::DB::Object::Debug = 0;

    return $user;
}

#-------------------------------------------------------------------------------
sub register_login {
    my $self    = shift;
    my $session = shift;

    my $session_data = $session->dataref; # DEBUG $session_data;

    my %data = (
        user_id     => $session_data->{UserProfile}->{id},
        address     => $session_data->{_SESSION_REMOTE_ADDR},
        browser     => $ENV{'HTTP_USER_AGENT'},
        session_id  => $session->id,
    ); # DEBUG \%data;

    LIMS::DB::Login->new(%data)->save;
}

#-------------------------------------------------------------------------------
sub update_password {
    my $self = shift;
    my $args = shift;

    eval {
        my $user = LIMS::DB::User->new(id => $args->{id})->load;
        $user->password($args->{pwd});
        $user->active('yes'); # in case re-activating expired account
        $user->save(changes_only => 1);
    };

    return $@ if $@;
}

#-------------------------------------------------------------------------------
sub update_location {
    my $self = shift;
    my $data = shift; # warn Dumper $args;
    
    my $user = $data->{user_profile}; # user object loaded in C::User
    
    eval {
        $user->user_location_id($data->{location_id});
        $user->save(changes_only => 1);
    };
	return $@ if $@;
}

#-------------------------------------------------------------------------------
sub delete_user {
    my $self    = shift;
    my $user_id = shift;

    eval {
        LIMS::DB::User->new(id => $user_id)->delete;
    };

    return $@ if $@;
}

#-------------------------------------------------------------------------------
sub get_all_users {
    my $self = shift;
    my $args = shift || {};

    $args->{require_objects} = 'user_location';
    $args->{sort_by} ||= 'username';
    
    my $data = LIMS::DB::User::Manager->get_users_iterator(%$args); # DEBUG $data;

    return $data;
}

#-------------------------------------------------------------------------------
sub update_user { 
    my $self = shift;
    my $data = shift; # $self->debug($data);

    my %args = ( class => 'User', data  => $data );
    
    return $self->update_object(\%args);
}

#-------------------------------------------------------------------------------
sub update_session_userid {
    my $self = shift;
    my $args = shift;
    
    LIMS::DB::Session::Manager->update_sessions(
        set   => { userid => uc $args->{userid} },
        where => [ id => $args->{session_id} ],
    ); # don't care about error return ?
    
    # update last_login timestamp:
    my $o = LIMS::DB::User->new(username => $args->{userid})->load;
    $o->last_login($self->time_now);
    $o->save(changes_only => 1);
}        

#-------------------------------------------------------------------------------
sub delete_user_permissions {
    my $self    = shift;
    my $user_id = shift;

    my %args = (
        where => [ user_id => $user_id ],
    );

    eval {
        LIMS::DB::UserPermission::Manager->delete_user_permissions(%args);
    };

    return $@ if $@;
}

#-------------------------------------------------------------------------------
sub get_user_function {
    my $self = shift;
    my $id   = shift;

    my $function = LIMS::DB::UserFunction->new(id => $id)->load; # DEBUG $function

    return $function;
}

#-------------------------------------------------------------------------------
sub get_user_functions {
    my $self = shift;
    my $args = shift || {};
    
    # get all user_functions rows as arrayref:
    my $data = LIMS::DB::UserFunction::Manager->get_user_functions(%$args); # DEBUG $data;

    return $data;
}

#-------------------------------------------------------------------------------
sub get_user_group {
    my $self = shift;
    my $id   = shift;

    my $group = LIMS::DB::UserGroup->new(id => $id)->load; # DEBUG $group

    return $group;
}

#-------------------------------------------------------------------------------
sub get_user_groups {
    my $self = shift;
    my $args = shift || {};

    # get all user_functions rows as arrayref:
    my $data = LIMS::DB::UserGroup::Manager->get_user_groups(%$args); # DEBUG $data;

    return $data;
}

#-------------------------------------------------------------------------------
sub get_user_group_functions {
    my $self       = shift;
    my $group_id   = shift;

    my %args = (
        query => [ 'group_id' => $group_id ],
        require_objects => 'function',
    );
    
    # get all user_group_function rows for submitted $group_id:
    my $data = LIMS::DB::UserGroupFunction::Manager->get_user_group_functions(%args); # DEBUG $data;

    return $data;
}

#-------------------------------------------------------------------------------
sub get_user_location {
    my $self = shift;
    my $id   = shift;

    my $location = LIMS::DB::UserLocation->new(id => $id)->load; # DEBUG $function

    return $location;
}

#-------------------------------------------------------------------------------
sub get_location_by_name {
    my ($self, $location_name) = @_;

    my $location
        = LIMS::DB::UserLocation->new(location_name => $location_name)->load; 

    return $location;
}

#-------------------------------------------------------------------------------
sub get_user_locations {
    my $self = shift;
    my $args = shift || {};

    $args->{sort_by} = 'location_name';
    
    # get all user_functions rows as arrayref:
    my $data = LIMS::DB::UserLocation::Manager->get_user_locations(%$args); # DEBUG $data;

    return $data;
}

#-------------------------------------------------------------------------------
sub get_user_permissions {
    my $self    = shift;
    my $user_id = shift;

    my %args = (
        query => [ 'user_id' => $user_id ],
    );

    # get user permissions (if any) from user_permission table:
    my $user_permissions = LIMS::DB::UserPermission::Manager
        ->get_user_permissions(%args);

    return $user_permissions;
}

#-------------------------------------------------------------------------------
sub update_user_functions {
    my $self = shift;
    my $data = shift; 

    my %args = ( class => 'UserFunction', data  => $data );
    
    return $self->update_object(\%args);
}

#-------------------------------------------------------------------------------
sub update_user_groups {
    my $self = shift;
    my $data = shift; # DEBUG $group;

    my %args = ( class => 'UserGroup', data  => $data );
    
    return $self->update_object(\%args);
}

#-------------------------------------------------------------------------------
sub update_user_group_functions {
    my $self = shift;
    my $args = shift; # DEBUG $args;

    my $db = $self->lims_db; # ie LIMS::DB->new_or_cached;

    my $group_id     = $args->{group_id} || die 'No group_id value passed to update_user_group_functions()';
    my $function_ids = $args->{function_ids}; # arrayref

    # methods in anon sub MUST use same $db as do_transaction():
    $db->do_transaction( sub {
        # first clear existing entries for this group_id:
        LIMS::DB::UserGroupFunction::Manager->delete_user_group_functions(
#            db    => $db,
            where => [ group_id => $group_id ],
        );

        foreach my $id (@$function_ids) { # warn $id, "\n";
            LIMS::DB::UserGroupFunction->new(
#                db          => $db,
                function_id => $id,
                group_id    => $group_id,
            )->save;
        }
    });

    # don't need return value unless error:
    return 'update_user_group_functions() error - ' . $db->error if $db->error;
}

#-------------------------------------------------------------------------------
sub update_user_locations {
    my $self = shift;
    my $data = shift; 

    my %args = ( class => 'UserLocation', data  => $data );
    
    return $self->update_object(\%args);
}

#-------------------------------------------------------------------------------
sub update_user_permissions {
    my $self = shift;
    my $args = shift; # DEBUG $args;

    my $db = $self->lims_db; # ie LIMS::DB->new_or_cached;
    
    my $user_id      = $args->{user_id} || die 'No user_id value passed to update_user_permissions()';
    my $function_ids = $args->{function_ids}; # arrayref

    # delete existing & update user_permissions in a single transaction:
    $db->do_transaction( sub {
        # first clear existing entries for this group_id:
        LIMS::DB::UserPermission::Manager->delete_user_permissions(
#            db    => $db,
            where => [ user_id => $user_id ],
        );

        # insert any new permissions:
        foreach my $id ( @{ $function_ids } ) {
            LIMS::DB::UserPermission->new(
#                db          => $db,
                function_id => $id,
                user_id     => $user_id,
            )->save;
        }
    });

    # don't need return value unless error:
    return 'update_user_permissions() error - ' . $db->error if $db->error;
}

#-------------------------------------------------------------------------------
sub verify_credentials {
    my ($self, $user_id, $pwd) = @_; # warn $user_id; warn $pwd;
    
    my ($first_name, $last_name) = split '\.', $user_id;
    
    my %args = (
        last_name  => $last_name,
        first_name => $first_name,
    );
    
    my $user = LIMS::DB::User->new(%args)->load(speculative => 1) || return 0;
    
    return 0 unless $user->active eq 'yes';
    
    my $password = LIMS::Local::Utils::sha1_digest($pwd);

    # delete user from sessions table (if exists) to prevent concurrent logins:
    if ( my $o = LIMS::DB::Session->new(
        userid => $user->username )->load(speculative => 1) ) {
        $o->delete;
    }
    
    return $user->username if $user->password eq $password;
}

1;
