#!/usr/bin/perl

#use utf8;
use strict;
use CGI;
use JSON;
use Time::localtime;
use Opals::Context;
use Opals::Template qw(
    tmpl_preference
);

use Opals::User qw(
    user_getInformationById
);
use Opals::Tb_Circulation qw(
    circ_getItemStatus
    circ_getItemInfo
    circ_setAvailable
    circ_record_ODL
    circ_processReturn
    circ_updateItemStatus
    circ_declareLost
);
use Opals::Date qw(
    date_getDeadLineDate
    date_deltaWorkDay   
    date_parse
    date_now
);
use Opals::Tb_Transactions qw(
    trans_doReverseLost
);
use Opals::Tb_Fines qw(
    fine_getFineRate
);
use Opals::Constant;

my $dbh = Opals::Context->dbh();
END { $dbh->disconnect(); }
my $cgi      = CGI->new;
my $todayStr   = date_now();
my $syspref = tmpl_preference($dbh);
my ($errCode, $ck, $user) = Opals::User::user_currentUser($dbh, $cgi);
my $loginuid   = $user->{'uid'};
my $fineRate =fine_getFineRate($dbh);

my $input = {};
my ($uid,$op);
my $returnMsg = [];
my $returnLostMsg = [];
my $returnDamageMsg = [];
my $returnOverdueMsg = [];

if ($ENV{'REQUEST_METHOD'} eq "POST") {
   $input = decode_json($cgi->param('POSTDATA'));
   $uid = $input->{'uid'};
   $op = $input->{'op'} || "";
   if ($op eq 'lost'){
       foreach my $bc(@{$input->{'bcList'}}){
           push @$returnLostMsg,processReturnLost($dbh,$bc,$uid);
       }
   }
   elsif($op eq 'damage'){
        foreach my $bc(@{$input->{'bcList'}}){
           push @$returnDamageMsg ,processReturnDamage($dbh,$bc,$uid);
       }
       $returnMsg =  $returnDamageMsg;
   }
   else {
       foreach my $bc(@{$input->{'bcList'}}){
           my $ret = processReturn($dbh,$bc);
           push @$returnMsg ,$ret;
           if ($ret->{'overdue'}){
               push @$returnOverdueMsg,$ret
           }
       }
   }
} #if ($ENV{'REQUEST_METHOD'} eq "POST")
print "Content-type: text/plain\n\n";
print   to_json({ 
    uid           => $uid,
    returnMsgList => $returnMsg,
    returnLostMsgList => $returnLostMsg,
    returnDamageMsgList => $returnDamageMsg,
    returnOverdueMsgList => $returnOverdueMsg,
});

#--------------------------------------------------------------------------------
sub processReturn {
    my ($dbh, $barcode) = @_;
    my $itemCircStatus  = circ_getItemStatus($dbh, $barcode);
    #my $return ; 
    my $itemInfo        = circ_getItemInfo($dbh, $barcode, undef);
    my $retval->{'itemInfo'} = {
        rid          => $itemInfo->{'rid'},
        title        => $itemInfo->{'title'},
        author       => $itemInfo->{'author'},
        pubName      => $itemInfo->{'pubName'},
        pubDate      => $itemInfo->{'pubDate'},
        price        => $itemInfo->{'price'},
        itemType     => $itemInfo->{'typeId'},
        callNumber   => $itemInfo->{'classNumber'},
        barcode      => $barcode
    };
    my $overdue =0;
    if ($itemCircStatus->{'status'} == IT_STAT_ONLOAN 
        || $itemCircStatus->{'status'} == IT_STAT_HAVE_RSVR
        || $itemCircStatus->{'status'} == IT_STAT_HOLD_EXPIRE) {

        if($itemCircStatus->{'status'} == IT_STAT_ONLOAN 
        || $itemCircStatus->{'status'} == IT_STAT_HAVE_RSVR){
            $retval->{'idloan'}=$itemCircStatus->{'l_idLoan'};
            $retval->{'loanInfo'}  =doReturn($dbh,$itemCircStatus->{'l_uid'}, $barcode,$todayStr);
            $overdue      = $retval->{'loanInfo'}->{'overdue'}>0?1:0;
            if ($overdue){
                $retval->{'overdue'} = $overdue;
                $itemInfo->{'numOfOverdue'} = $retval->{'loanInfo'}->{'overdue'};
                $retval->{'odl_id'} = $retval->{'loanInfo'}->{'odl_id'};
                $retval->{'fineType'} = "overdue";
                $retval->{'fineRateTbl'} = getFineRate("overdue",$itemInfo);
            }
            $retval->{'uid'} = $itemCircStatus->{'l_uid'};
        }
        my $reserveList  = $itemCircStatus->{'reserveList'};
        # Check if item is on reserved
        if (!$reserveList || scalar(@$reserveList) == 0) {
            $retval->{'retStatusCode'} = "1$overdue";
        }
        else {
            if($itemCircStatus->{'status'} == IT_STAT_HOLD_EXPIRE){
                 $retval->{'retStatusCode'} = "22";
            }
            else{
               $retval->{'retStatusCode'} = "2$overdue";
            }
            my ($idReserve, $uidReserve, $reserveExpiry, $dateExpiry);
            $idReserve      = $reserveList->[0]->{'idReserve'};
            $uidReserve     = $reserveList->[0]->{'uid'};
            $reserveExpiry  = $reserveList->[0]->{'dateExpiry'};
            $itemInfo   = circ_getItemInfo($dbh, $barcode, $uidReserve);
            $dateExpiry =date_getDeadLineDate($itemInfo->{'holdPeriod'}, $todayStr);
            if ($dateExpiry > $reserveExpiry) {
                $dateExpiry = $reserveExpiry;
            }
            circ_fillReserve($dbh,$uidReserve ,$itemCircStatus->{'rid'});#Ha
            circ_placeHold($dbh, $idReserve, $todayStr, $dateExpiry);
            my ($reserveUser, $guardian) = 
                user_getInformationById($dbh, $uidReserve);
            $retval->{'reserveUserUid'}   =  $uidReserve;
            $retval->{'reserveUserFirstname'}   = $reserveUser->{'firstname'};
            $retval->{'reserveUserLastname'}    = $reserveUser->{'lastname'};
        }
    }
    elsif ($itemCircStatus->{'status'} == IT_STAT_LOST) {
        $retval->{'retStatusCode'} = '30';
        circ_setAvailable($dbh,$barcode,IT_STAT_LOST);
        trans_doReverseLost($dbh,$barcode,$loginuid);
        my $loanUId=  circ_getLastLoanUId($dbh,$barcode);
        if ($loanUId){
            my ($lostUser, $guardian) = 
                user_getInformationById($dbh, $loanUId);
            $retval->{'lostUserId'}   =  $loanUId;
            $retval->{'lostFirstname'} =  $lostUser->{'firstname'};
            $retval->{'lostLastname'} =  $lostUser->{'lastname'};
        }
    }
    elsif ($itemCircStatus->{'status'} == IT_STAT_NOEXIST) {
        $retval->{'retStatusCode'} = '40';
    }
    elsif ($itemCircStatus->{'status'} == IT_STAT_ONHOLD) {
        $retval->{'retStatusCode'} ='50';

        my $holdList = $itemCircStatus->{'holdList'};
        $retval->{'holdUserUid'}       = $holdList->[0]->{'uid'};
        $retval->{'holdUserFirstname'} = $holdList->[0]->{'firstname'};
        $retval->{'holdUserLastname'}  = $holdList->[0]->{'lastname'};
    }
    elsif($itemCircStatus->{'status'} == IT_STAT_MISSING) {
        $retval->{'retStatusCode'} ='70';
        if($input->{'Condition'} eq "Damaged"){
            circ_updateItemStatus($dbh,$barcode,ITEM_DAMAGED);
            #???????????? opl_odl  
        }
        else{
            circ_setAvailable($dbh,$barcode,IT_STAT_MISSING);
        }
    }
    elsif($itemCircStatus->{'status'} == IT_STAT_HOLD_EXPIRE){

    }
    else {
        $retval->{'retStatusCode'} ='60';
    }
    return ($retval);
}
#-------------------------------------------------------------------------------
sub doReturn{
    my ($dbh,$uid,$barcode,$dateReturn)=@_;
    
    my $sth = $dbh->prepare("select id, dateDue from tb_loan where uid=? && barcode=? && dateReturn is null");
    $sth->execute($uid,$barcode);
    my $rec = $sth->fetchrow_hashref;
    my $idloan   = $rec->{'id'};
    my $dateDue    = date_parse($rec->{'dateDue'});
    my $dateRet = date_parse($dateReturn);
    my $numOfOverdue = date_deltaWorkDay($dateDue,$dateRet);
    $numOfOverdue=0 if ($numOfOverdue<=0);
    my $odl_id =0;
    # Even overduefine = 0, it is still recorded to keep track how many times
    # user make overdue
    if ($numOfOverdue>0){
        $odl_id = circ_record_ODL($dbh,$idloan,'overdue',$numOfOverdue);
    }
    my $return={lid=>$idloan,overdue=>$numOfOverdue,odl_id=>$odl_id};
    circ_processReturn($dbh,$uid,$barcode,$dateReturn);
    return $return;
}

sub processReturnLost {
    my ($dbh,$barcode,$uid) = @_;
    my $ret;
    $barcode    =~ s/^\s+|\s+$//g;
    my $itemCircStatus = circ_getItemStatus($dbh,$barcode );
    $ret->{'uid'} = $itemCircStatus->{'l_uid'};
    $ret->{'lid'} = $itemCircStatus->{'l_idLoan'},
    my $itemInfo = circ_getItemInfo($dbh,$barcode, $uid);
    my ($lostUser, $guardian) = user_getInformationById($dbh, $itemCircStatus->{'l_uid'});
    $ret->{'lostdate'} = $todayStr;
    $ret->{'firstname'} = $lostUser->{'firstname'};
    $ret->{'lastname'} = $lostUser->{'lastname'};
    # check if item exists
    if($itemCircStatus->{'status'} == IT_STAT_NOEXIST){
        $ret->{'lostMsgCode'} ="02";
    }
    # check if item is already marked as lost
    elsif($itemCircStatus->{'status'} == IT_STAT_LOST){
        $ret->{'lostMsgCode'}="03";
    }
    else{
        # record lost if item is currently on loan
        if($itemCircStatus->{'status'} == IT_STAT_ONLOAN){
            $ret->{'lost_odl_id'} = circ_record_ODL($dbh,$itemCircStatus->{'l_idLoan'},'lost',0);
            $ret->{'odl_id'} = $ret->{'lost_odl_id'};
        }
        # cancel hold or reserve if any...
        if($itemCircStatus->{'status'} == IT_STAT_ONHOLD){
            #circ_cancelHoldByRid($dbh,$itemInfo->{'rid'});
        }
        if($itemCircStatus->{'status'} == IT_STAT_HAVE_RSVR){
            #circ_cancelReserveByRid($dbh,$itemInfo->{'rid'});
        }
        circ_declareLost($dbh,$barcode);
        $ret->{'lostMsgCode'}="01";
    }
    $ret->{'itemInfo'} = {
        rid          => $itemInfo->{'rid'},
        title        => $itemInfo->{'title'},
        author       => $itemInfo->{'author'},
        pubName      => $itemInfo->{'pubName'},
        pubDate      => $itemInfo->{'pubDate'},
        price        => $itemInfo->{'price'},
        itemType     => $itemInfo->{'typeId'},
        callNumber   => $itemInfo->{'classNumber'},
        barcode      => $barcode
    };
    $ret->{'retStatusCode'} = $ret->{'lostMsgCode'};
    $ret->{'fineType'} = "lost";
    $ret->{'fineRateTbl'} = getFineRate("lost",$itemInfo);
    return $ret;
}
sub processReturnDamage {
    my ($dbh,$barcode,$uid) = @_;
    my $ret;
    my $itemCircStatus = circ_getItemStatus($dbh,$barcode);
    $ret = processReturn($dbh,$barcode);
    if($itemCircStatus->{'status'} == IT_STAT_ONLOAN){
        $ret->{'damage_odl_id'} = circ_record_ODL($dbh,$itemCircStatus->{'l_idLoan'},'damaged',0);
        $ret->{'odl_id'} = $ret->{'damage_odl_id'};
    }
    my $itemInfo = circ_getItemInfo($dbh,$barcode, $uid);
    $ret->{'fineType'} = "damage";
    $ret->{'fineRateTbl'} = getFineRate("damage",$itemInfo);
    circ_updateItemStatus($dbh,$barcode,ITEM_DAMAGED,$uid);
    #$ret->{'retStatusCode'} = "04";
    return $ret;
}
sub circ_getLastLoanUId {
    my ($dbh,$barcode) = @_;
    my $sth = $dbh->prepare("select uid from tb_loan where barcode=? order by dateReturn desc limit 1");
    $sth->execute($barcode);
    my $rec =$sth->fetchrow_hashref; 
    my $uid = 0;
    if($rec){
        $uid = $rec->{'uid'}; 
    }
    $sth->finish;
    return $uid;
}

sub getFineRate {
    my ($type,$item) = @_;
    my $itemType = $item->{'typeId'};
    my $rateTbl = {};
    foreach my $f (@$fineRate){
        if ($itemType eq $f->{'itemType'}){
            $f->{'fee'} = 0.00;
            if ($type eq "lost"){
                $f->{'fee'} = sprintf '%.2f',$item->{'price'} || 0.00;
            }
            if ($type eq "overdue"){
                if ($f->{'overdue'}){
                    $f->{'fee'}=($item->{'numOfOverdue'} && $item->{'numOfOverdue'}>0)?$f->{'overdue'}*$item->{'numOfOverdue'}:$f->{'overdue'};
                    $f->{'fee'} = ($f->{'maxOverdue'} && $f->{'maxOverdue'}<$f->{'fee'})? $f->{'maxOverdue'}:$f->{'fee'};
                }
                else{
                    $f->{'fee'} = 0.00;
                }
            }
            if ($type eq "damage"){
                $f->{'fee'} = ($f->{'damage'} && $f->{'damage'} > 0)?$f->{'damage'}: (sprintf '%.2f',$item->{'price'} || 0.0 ) ;
                #if ($f->{'damage'} && $f->{'damage'}>0){$f->{'fee'}=$f->{'damage'};}else{$f->{'fee'}=sprintf '%.2f',$item->{'price'}||0.0;}
            }
            $f->{'fee'} = $f->{'fee'}>0? (sprintf '%.2f',$f->{'fee'}): 0.00;
            $f->{'charge'} = (sprintf '%.2f',$f->{'fee'}) || 0.00;
            $rateTbl = $f;
        }
    }
    return $rateTbl;
}
