package C4::Utils::DataTables::Members;

use Modern::Perl;
use C4::Context;
use C4::Utils::DataTables;
use Koha::DateUtils;

sub search {
    my ( $params ) = @_;
    my $searchmember = $params->{searchmember};
    my $firstletter = $params->{firstletter};
    my $categorycode = $params->{categorycode};
    my $branchcode = $params->{branchcode};
    my $searchtype = $params->{searchtype} || 'contain';
    my $searchfieldstype = $params->{searchfieldstype} || 'standard';
    my $has_permission = $params->{has_permission};
    my $dt_params = $params->{dt_params};

    unless ( $searchmember ) {
        $searchmember = $dt_params->{sSearch} // '';
    }

    # If branches are independent and user is not superlibrarian
    # The search has to be only on the user branch
    my $userenv = C4::Context->userenv;
    my $logged_in_user = Koha::Patrons->find( $userenv->{number} );
    my @restricted_branchcodes = $logged_in_user->libraries_where_can_see_patrons;

    my ($sth, $query, $iTotalQuery, $iTotalRecords, $iTotalDisplayRecords);
    my $dbh = C4::Context->dbh;

    # Get the module_bit from a given permission code
    if ( $has_permission ) {
        ($has_permission->{module_bit}) = $dbh->selectrow_array(q|
            SELECT bit FROM userflags WHERE flag=?
        |, undef, $has_permission->{permission});
    }

    # Get the iTotalRecords DataTable variable
    $iTotalQuery = "SELECT COUNT(borrowers.borrowernumber) FROM borrowers";
    if ( $has_permission ) {
        $iTotalQuery .= ' LEFT JOIN user_permissions on borrowers.borrowernumber=user_permissions.borrowernumber';
    }

    my (@where, @conditions);
    if ( @restricted_branchcodes ) {
        push @where, "borrowers.branchcode IN (" . join( ',', ('?') x @restricted_branchcodes ) . ")";
        push @conditions, @restricted_branchcodes;
    }
    if ( $has_permission ) {
        push @where, '( borrowers.flags = 1 OR borrowers.flags & (1 << ?) OR module_bit=? AND code=? )';
        push @conditions, ($has_permission->{module_bit}) x 2, $has_permission->{subpermission};
    }
    $iTotalQuery .= ' WHERE ' . join ' AND ', @where if @where;
    ($iTotalRecords) = $dbh->selectrow_array( $iTotalQuery, undef, @conditions );

    # Do that after iTotalQuery!
    if ( defined $branchcode and $branchcode ) {
        @restricted_branchcodes = @restricted_branchcodes
            ? grep ({ $_ eq $branchcode } @restricted_branchcodes)
                ? ($branchcode)
                : (undef) # Do not return any results
            : ($branchcode);
    }

    if ( $searchfieldstype eq 'dateofbirth' ) {
        # Return an empty list if the date of birth is not correctly formatted
        $searchmember = eval { output_pref( { str => $searchmember, dateformat => 'iso', dateonly => 1 } ); };
        if ( $@ or not $searchmember ) {
            return {
                iTotalRecords        => $iTotalRecords,
                iTotalDisplayRecords => 0,
                patrons              => [],
            };
        }
    }

    my $select = "SELECT
        borrowers.borrowernumber, borrowers.surname, borrowers.firstname,
        borrowers.othernames,
        borrowers.flags,
        borrowers.streetnumber, borrowers.streettype, borrowers.address,
        borrowers.address2, borrowers.city, borrowers.state, borrowers.zipcode,
        borrowers.country, cardnumber, borrowers.dateexpiry,
        borrowers.borrowernotes, borrowers.branchcode, borrowers.email,
        borrowers.userid, borrowers.dateofbirth, borrowers.categorycode,
        categories.description AS category_description, categories.category_type,
        branches.branchname, borrowers.phone";
    my $from = "FROM borrowers
                LEFT JOIN branches ON borrowers.branchcode = branches.branchcode
                LEFT JOIN categories ON borrowers.categorycode = categories.categorycode";
    if ( $has_permission ) {
        $from .= '
                LEFT JOIN user_permissions on borrowers.borrowernumber=user_permissions.borrowernumber';
    }
    my @where_args;
    my @where_strs;
    if(defined $firstletter and $firstletter ne '') {
        push @where_strs, "borrowers.surname LIKE ?";
        push @where_args, "$firstletter%";
    }
    if(defined $categorycode and $categorycode ne '') {
        push @where_strs, "borrowers.categorycode = ?";
        push @where_args, $categorycode;
    }
    if(@restricted_branchcodes ) {
        push @where_strs, "borrowers.branchcode IN (" . join( ',', ('?') x @restricted_branchcodes ) . ")";
        push @where_args, @restricted_branchcodes;
    }

    my $searchfields = {
        standard => C4::Context->preference('DefaultPatronSearchFields') || 'surname,firstname,othernames,cardnumber,userid',
        email => 'email,emailpro,B_email',
        borrowernumber => 'borrowernumber',
        phone => 'phone,phonepro,B_phone,altcontactphone,mobile',
        address => 'streetnumber,streettype,address,address2,city,state,zipcode,country',
    };

    # * is replaced with % for sql
    $searchmember =~ s/\*/%/g;

    # split into search terms
    my @terms;
    # consider coma as space
    $searchmember =~ s/,/ /g;
    if ( $searchtype eq 'contain' ) {
       @terms = split / /, $searchmember;
    } else {
       @terms = ($searchmember);
    }

    foreach my $term (@terms) {
        next unless $term;

        my $term_dt = eval { local $SIG{__WARN__} = {}; output_pref( { str => $term, dateonly => 1, dateformat => 'sql' } ); };

        if ($term_dt) {
            $term = $term_dt;
        } else {
            $term .= '%'    # end with anything
              if $term !~ /%$/;
            $term = "%$term"    # begin with anythin unless start_with
              if $searchtype eq 'contain' && $term !~ /^%/;
        }

        my @where_strs_or;
        if ( defined $searchfields->{$searchfieldstype} ) {
            for my $searchfield ( split /,/, $searchfields->{$searchfieldstype} ) {
                push @where_strs_or, "borrowers." . $dbh->quote_identifier($searchfield) . " LIKE ?";
                push @where_args, $term;
            }
        } else {
            push @where_strs_or, "borrowers." . $dbh->quote_identifier($searchfieldstype) . " LIKE ?";
            push @where_args, $term;
        }


        if ( $searchfieldstype eq 'standard' and C4::Context->preference('ExtendedPatronAttributes') and $searchmember ) {
            my @matching_borrowernumbers = Koha::Patrons->filter_by_attribute_value($searchmember)->get_column('borrowernumber');

            for my $borrowernumber ( @matching_borrowernumbers ) {
                push @where_strs_or, "borrowers.borrowernumber = ?";
                push @where_args, $borrowernumber;
            }
        }

        push @where_strs, '('. join (' OR ', @where_strs_or) . ')'
            if @where_strs_or;
    }

    if ( $has_permission ) {
        push @where_strs, '( borrowers.flags = 1 OR borrowers.flags & (1 << ?) OR module_bit=? AND code=? )';
        push @where_args, ($has_permission->{module_bit}) x 2, $has_permission->{subpermission};
    }

    my $where = @where_strs ? " WHERE " . join (" AND ", @where_strs) : undef;
    my $orderby = dt_build_orderby($dt_params);

    my $limit;
    # If iDisplayLength == -1, we want to display all patrons
    if ( !$dt_params->{iDisplayLength} || $dt_params->{iDisplayLength} > -1 ) {
        # In order to avoid sql injection
        $dt_params->{iDisplayStart} =~ s/\D//g if defined($dt_params->{iDisplayStart});
        $dt_params->{iDisplayLength} =~ s/\D//g if defined($dt_params->{iDisplayLength});
        $dt_params->{iDisplayStart} //= 0;
        $dt_params->{iDisplayLength} //= 20;
        $limit = "LIMIT $dt_params->{iDisplayStart},$dt_params->{iDisplayLength}";
    }

    $query = join(
        " ",
        ($select ? $select : ""),
        ($from ? $from : ""),
        ($where ? $where : ""),
        ($orderby ? $orderby : ""),
        ($limit ? $limit : "")
    );
    $sth = $dbh->prepare($query);
    $sth->execute(@where_args);
    my $patrons = $sth->fetchall_arrayref({});

    # Get the iTotalDisplayRecords DataTable variable
    $query = "SELECT COUNT(borrowers.borrowernumber) " . $from . ($where ? $where : "");
    $sth = $dbh->prepare($query);
    $sth->execute(@where_args);
    ($iTotalDisplayRecords) = $sth->fetchrow_array;

    # Get some information on patrons
    foreach my $patron (@$patrons) {
        my $patron_object = Koha::Patrons->find( $patron->{borrowernumber} );
        $patron->{overdues} = $patron_object->get_overdues->count;
        $patron->{issues} = $patron_object->checkouts->count;
        my $balance = $patron_object->account->balance;
        # FIXME Should be formatted from the template
        $patron->{fines} = sprintf("%.2f", $balance);

        if( $patron->{dateexpiry} ) {
            # FIXME We should not format the date here, do it in template-side instead
            $patron->{dateexpiry} = output_pref( { dt => scalar dt_from_string( $patron->{dateexpiry}, 'iso'), dateonly => 1} );
        } else {
            $patron->{dateexpiry} = '';
        }
    }

    return {
        iTotalRecords => $iTotalRecords,
        iTotalDisplayRecords => $iTotalDisplayRecords,
        patrons => $patrons
    }
}

1;
__END__

=head1 NAME

C4::Utils::DataTables::Members - module for using DataTables with patrons

=head1 SYNOPSIS

This module provides (one for the moment) routines used by the patrons search

=head2 FUNCTIONS

=head3 search

    my $dt_infos = C4::Utils::DataTables::Members->search($params);

$params is a hashref with some keys:

=over 4

=item searchmember

  String to search in the borrowers sql table

=item firstletter

  Introduced to contain 1 letter but can contain more.
  The search will done on the borrowers.surname field

=item categorycode

  Search patrons with this categorycode

=item branchcode

  Search patrons with this branchcode

=item searchtype

  Can be 'start_with' or 'contain' (default value). Used for the searchmember parameter.

=item searchfieldstype

  Can be 'standard' (default value), 'email', 'borrowernumber', 'phone', 'address' or 'dateofbirth', 'sort1', 'sort2'

=item dt_params

  Is the reference of C4::Utils::DataTables::dt_get_params($input);

=cut

=back

=head1 LICENSE

This file is part of Koha.

Copyright 2013 BibLibre

Koha is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.

Koha is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Koha; if not, see <http://www.gnu.org/licenses>.
