#!/usr/bin/perl

use strict;

use Date::Format 'time2str';
use Date::Parse 'str2time';
use Getopt::Long;
use Cpanel::JSON::XS;
use Net::HTTPS::Any qw(https_post);
use Time::Local;

use FS::Record qw(qsearchs dbh);
use FS::UID qw(adminsuidsetup);
use FS::cdr;
use FS::cdr_batch;

sub usage {
  "Usage:
freeside-cdr-portaone-import -x exportnum [-s startdate] [-e enddate] [-v] freesideuser

freeside-cdr-portaone-import -h 'your.domain.com:443' -u switchusername -p switchpass 
  [-s startdate] [-e enddate] [-v] freesideuser
";
}

my ($host,$username,$password,$startdate,$enddate,$verbose,$exportnum);
GetOptions(
  "enddate=s"   => \$enddate,
  "host=s"      => \$host,
  "password=s"  => \$password,
  "startdate=s" => \$startdate,
  "username=s"  => \$username,
  "verbose"     => \$verbose,
  "x=s"         => \$exportnum,
);

my $fsuser = $ARGV[-1];

die usage() unless $fsuser;

adminsuidsetup($fsuser);

my $port = 443;
if ($host =~ /^(.*)\:(.*)$/) {
  $host = $1;
  $port = $2;
}

if ($exportnum) {
  my $export = qsearchs('part_export', { 'exportnum' => $exportnum });
  die "Export not found" unless $export;
  $host = $export->machine;
  $port = $export->option('port');
  $username = $export->option('username');
  $password = $export->option('password');
  die "Auth info not fully specified in export"
    unless $host && $port && $username && $password;
}

die usage() unless $host && $password && $username;

if ($startdate) {
  $startdate = str2time($startdate) or die "Can't parse startdate $startdate";
  $startdate = time2str("%Y-%m-%d %H:%M:%S",$startdate);
}
unless ($startdate) {
  my $lastbatch = qsearchs({
    'table'     => 'cdr_batch',
    'hashref'   => { 'cdrbatch' => {op=>'like', value=>'portaone-import%'}},
    'order_by'  => 'ORDER BY _date DESC LIMIT 1',
  });
  $startdate = time2str("%Y-%m-%d %H:%M:%S", $lastbatch->_date) if $lastbatch;
}
$startdate ||= '2010-01-01 00:00:00'; #seems decently in the past

my @now = localtime();
my $now = timelocal(0,0,0,$now[3],$now[4],$now[5]); #most recent midnight
if ($enddate) {
  $enddate = str2time($enddate) or die "Can't parse enddate $enddate";
  $now = $enddate;
  $enddate = time2str("%Y-%m-%d %H:%M:%S",$enddate);
}
$enddate ||= time2str("%Y-%m-%d %H:%M:%S",$now);


$FS::UID::AutoCommit = 0;

my $cdrbatchname = 'portaone-import-'. time2str('%Y/%m/%d-%T',$now);
die "Batch $cdrbatchname already exists, please specify a different end date.  " . usage()
  if FS::cdr_batch->row_exists('cdrbatch = ?', $cdrbatchname);
my $cdr_batch = new FS::cdr_batch({ 
  'cdrbatch' => $cdrbatchname,
  '_date'    => $now,
});
my $error = $cdr_batch->insert;
if ($error) {
  dbh->rollback;
  die "Error creating batch: $error";
}

print "Downloading records from $startdate to $enddate\n" if $verbose;

my $auth_info; # needs to be declared undef for call_api
$auth_info = call_api('Session','login',{
  'login'    => $username,
  'password' => $password,
});

my $results = {};
my $custlist = call_api('Customer','get_customer_list');
my @custnum = map { $_->{'i_customer'} } @{$custlist->{'customer_list'}};
foreach my $custnum (@custnum) {
  print "Retrieving for customer $custnum\n" if $verbose;
  my $step = 500; # too many records was crashing server, so we request in chunks
  my $lastcount = $step; # to get the while loop rolling
  my $totalcount = 0; # for verbose display only
  my $offset = 0;
  while ($lastcount == $step) {
    my $xdrs = call_api('Customer','get_customer_xdrs',{
      'i_customer' => $custnum,
      'from_date'  => $startdate,
      'to_date'    => $enddate,
      'cdr_entity' => 'A',
      'limit'      => $step,
      'offset'     => $offset,
    });
    my @xdrs = @{$xdrs->{'xdr_list'}};
    print "Retrieved ".@xdrs." records\n" if $verbose && @xdrs;
    my $skipped = 0; # for verbose display only
    foreach my $xdr (@xdrs) {
      if ( FS::cdr->row_exists('uniqueid = ?', $xdr->{'i_xdr'}) ) {
        $skipped += 1;
        next;
      }
      my $desc = $xdr->{'country'};
      if ($xdr->{'subdivision'}) {
        $desc = ', ' . $desc if $desc;
        $desc = $xdr->{'subdivision'} . $desc;
      }
      if ($xdr->{'description'}) {
        $desc = ' (' . $desc . ')' if $desc;
        $desc = $xdr->{'description'} . $desc;
      }
      my $cdr = FS::cdr->new ({
        'cdrbatchnum'             => $cdr_batch->cdrbatchnum,
        'uniqueid'                => $xdr->{'i_xdr'},
        'src'                     => $xdr->{'CLI'},
        'dst'                     => $xdr->{'CLD'},
        'upstream_price'          => $xdr->{'charged_amount'},
        'startdate'               => $xdr->{'unix_connect_time'},
        'enddate'                 => $xdr->{'unix_disconnect_time'},
        'accountcode'             => $xdr->{'account_id'},
        'billsec'                 => $xdr->{'charged_quantity'},
        'upstream_dst_regionname' => $desc,
      });
      $error = $cdr->insert;
      if ($error) {
        dbh->rollback;
        die "Error inserting cdr: $error";
      }
    } #foreach $xdr
    print "Skipped $skipped duplicate records\n" if $verbose && $skipped;
    $totalcount += @xdrs - $skipped;
    $lastcount = @xdrs;
    $offset += $step;
  } #while $lastcount == $step
  print scalar($totalcount) . " records inserted\n" if $verbose;
} #foreach $custnum

call_api('Session','logout',$auth_info);

### Full list of fields returned by API:
#i_xdr                     int      The unique ID of the xdr record
#account_id	              int      The unique ID of the account database record
#CLI                       string   Calling Line Identification
#CLD                       string   Called Line Identification
#charged_amount            float    Amount charged
#charged_quantity          int      Units charged
#country                   string   Country
#subdivision               string   Country subdivision
#description               string   Destination description
#disconnect_cause          string   The code of disconnect cause
#bill_status               string   Call bill status
#disconnect_reason         string   Call disconnect reason
#connect_time              dateTime Call connect time
#unix_connect_time         int      Call connect time (expressed in Unix time format - seconds since epoch)
#disconnect_time           dateTime Call disconnect time 
#unix_disconnect_time      int      Call disconnect time (expressed in Unix time format - seconds since epoch)
#bill_time                 dateTime Call bill time 
#bit_flags                 int      Extended information how the service was used; the integer field that should be treated as a bit-map. Each currently used bit is listed in the Transaction_Flag_Types table (bit_offset indicates position)
#call_recording_url        string   Path to recorded .wav files
#call_recording_server_url string   URL to the recording server 

dbh->commit;

exit;

sub call_api {
  my ($service,$method,$params) = @_;
  my %auth_info = $auth_info ? ('auth_info' => encode_json($auth_info)) : ();
  $params ||= {};
  print "Calling $service/$method\n" if $verbose;
  my ( $page, $response, %reply_headers ) = https_post(
    'host'    => $host,
    'port'    => $port,
    'path'    => '/rest/'.$service.'/'.$method.'/',
    'args'    => [ %auth_info, 'params' => encode_json($params) ],
  );
  return decode_json($page) if $response eq '200 OK';
  dbh->rollback;
  if ($response =~ /^500/) {
    my $error = decode_json($page);
    die "Server returned error during $service/$method: ".$error->{'faultstring'}
      if $error->{'faultcode'};
  }
  die "Bad response from server during $service/$method: $response";
}


