package Data::OFAC::SDN;
use strict;
use warnings;
use DBI;
use LWP::UserAgent;
use Archive::Extract;
use Carp;
use Text::CSV;
use Data::Dumper;
use Data::OFAC::SDN::Schema;

use constant { SDNURL => 'http://www.treasury.gov/ofac/downloads/sdall.zip' };

sub new {
  my $class = shift;

  my %args = @_;

  my $self = bless {}, $class;

  while ( my ( $key, $value ) = each %args ) {
    $self->{$key} = $value;
  }

  # This logic is probably convoluted.

  my $databaseupdated;

  $databaseupdated = -1 unless ( -f $self->{p}->{database_file} );

  $self->{db} = Data::OFAC::SDN::Schema->connect( "dbi:SQLite:" . $self->{p}->{database_file} )
    unless defined $self->{db};

  if ( defined $databaseupdated && $databaseupdated == -1 ) {

    # Force database update if no file is present.
    $self->updateDatabase;
    $databaseupdated = 1;
  }

  unless ( defined $databaseupdated || $self->{p}->{auto_update} eq '0' ) {

    my $lupdate = $self->{db}->resultset('Lastupdate')->search( {}, { order_by => 'LASTUPDATEDATETIME' } );
    my $lastupdate = $lupdate->single;

    #warn Dumper $lastupdate;
    #$lastupdate->result_source->schema;
    if ( defined $lastupdate
      && ( time() - $lastupdate->lastupdatedatetime ) >= ( $self->{p}->{auto_update_frequency} * 3600 ) )
    {
      $self->updateDatabase();
    }

    return $self;
  }
}

sub updateDatabase {
  my $self = shift;

  my $lwp = LWP::UserAgent->new();
  $lwp->timeout(10);
  $lwp->env_proxy;

  my $rsp = $lwp->get(SDNURL);

  if ( $rsp->is_success ) {
    $self->buildDatabase( $rsp->decoded_content );
  }
  else {
    croak( "Database failed to update: " . $rsp->status_line );
  }

  return;
}

sub buildDatabase {
  my $self = shift;
  my $zip  = shift;

  if ( $^O eq 'MSWin32' ) {
    $self->{tempdir} = $ENV{TEMP};
  }
  else {
    $self->{tempdir} = $ENV{TMPDIR};
  }

  my $sdnzip = ( $self->{tempdir} || '/tmp/' ) . "sdn.zip";

  open( my $fh, ">", $sdnzip )
    or die "Could not open file: " . $!;

  binmode $fh;
  print $fh $zip;
  close $fh;

  my $sdnfiles = ( $self->{tempdir} || '/tmp/' );

  my $ae = Archive::Extract->new( archive => $sdnzip );

  $ae->extract( to => $sdnfiles );

  $self->{dbh} = DBI->connect( 'dbi:SQLite:' . ( $self->{p}->{database_file} ), "", "", { RaiseError => 1 } );

  unless ( $self->{dbh} ) {
    croak "Could not open the SQLite database: " . DBI->errstr();
  }

  $self->SDNTable;
  $self->ALTTable;
  $self->ADDTable;
  $self->LASTUPDATETable;
  $self->SDNCOMMENTSTable;

  $self->{dbh}->disconnect;

  $self->{csv} = Text::CSV->new();

  # Process the SDN Table

  my @cols = qw{ent_num SDN_Name SDN_Type Program Title Call_Sign Vess_type
    Tonnage GRT Vess_flag Vess_owner remarks};

  $self->processTable( $sdnfiles . "/sdn.csv", 'Sdn', @cols );

  @cols = qw{ent_num SDN_Name SDN_Type Program Title Call_Sign Vess_type
    Tonnage GRT Vess_flag Vess_owner remarks};

  $self->processTable( $sdnfiles . "/sdn_comments.csv",
    'SdnComment', @cols );

  # Process the ADD Table

  @cols = qw{ent_num add_num CityStateProvincePostalCode Country Add_remarks};

  $self->processTable( $sdnfiles . "/add.csv",
    'Address',
    @cols );

  # Process the ALT Table

  @cols = qw{ent_num alt_num alt_type alt_name alt_remarks};

  $self->processTable( $sdnfiles . "/alt.csv", 'Alt', @cols );

  my $lu = $self->{db}->resultset('Lastupdate')->first;
  $lu->delete if defined $lu;

  $lu = $self->{db}->resultset('Lastupdate')->create( { lastupdatedatetime => time() } );
  $lu->update;

  return;

}

sub processTable {
  my $self     = shift;
  my $file     = shift;
  my $table    = shift;

  my @cols = @_;

  open( my $fh, "<", $file ) or croak "Could not open the SDN file: " . $file . "( " . $! . " )";

  while ( my $row = $self->{csv}->getline($fh) ) {

    my $r = $self->{db}->resultset($table)->create(
      {
        map { ( lc($cols[$_]) => ( ( defined $row->[$_] && $row->[$_] eq '-0- ' ) ? undef : $row->[$_] ) ) } 0 .. ( scalar(@cols) - 1 )
      }
    );
    $r->update;
  }

  close $fh;

  return;
}

sub SDNTable {
  my $self = shift;

  $self->{dbh}->do("drop table if exists SDN");
  $self->{dbh}->do( "
CREATE TABLE SDN  (
        ent_num   	NUMERIC NOT NULL,
        SDN_Name  	TEXT,
        SDN_Type  	TEXT,
        Program   	TEXT,
        Title     	TEXT,
        Call_Sign 	TEXT,
        Vess_type 	TEXT,
        Tonnage   	TEXT,
        GRT       	TEXT,
        Vess_flag 	TEXT,
        Vess_owner	TEXT,
        remarks   	TEXT,
        sdn_name_phonetic_phonix   TEXT,
        title_phonetic_phonix      TEXT,
        PRIMARY KEY(ent_num)
)" );
  return;
}

sub SDNCOMMENTSTable {
  my $self = shift;

  $self->{dbh}->do("drop table if exists SDN_COMMENTS");
  $self->{dbh}->do( "
CREATE TABLE SDN_COMMENTS  (
        ent_num   	NUMERIC NOT NULL,
        SDN_Name  	TEXT,
        SDN_Type  	TEXT,
        Program   	TEXT,
        Title     	TEXT,
        Call_Sign 	TEXT,
        Vess_type 	TEXT,
        Tonnage   	TEXT,
        GRT       	TEXT,
        Vess_flag 	TEXT,
        Vess_owner	TEXT,
        remarks   	TEXT,
        sdn_name_phonetic_phonix   TEXT,
        title_phonetic_phonix      TEXT,
        PRIMARY KEY(ent_num)
)" );
  return;
}

sub ADDTable {
  my $self = shift;

  $self->{dbh}->do("drop table if exists \"ADDRESS\"");

  $self->{dbh}->do( "
CREATE TABLE \"ADDRESS\"  (
        ent_num                    	NUMERIC NOT NULL,
        add_num                    	NUMERIC NOT NULL,
        CityStateProvincePostalCode	TEXT,
        Country                    	TEXT,
        Add_remarks                	TEXT,
        CityStateProvincePostalCode_phonetic_phonix	TEXT,
        Country_phonetic_phonix                    	TEXT,
        FOREIGN KEY(ent_num)
        REFERENCES SDN(ent_num)
        ON UPDATE NO ACTION
        ON DELETE NO ACTION
)" );
  return;
}

sub ALTTable {
  my $self = shift;

  $self->{dbh}->do("drop table if exists ALT");
  $self->{dbh}->do( "
CREATE TABLE ALT  (
        ent_num    	NUMERIC NOT NULL,
        alt_num    	NUMERIC NOT NULL,
        alt_type   	TEXT,
        alt_name   	TEXT,
        alt_remarks	TEXT,
        alt_name_phonetic_phonix   TEXT,
        FOREIGN KEY(ent_num)
        REFERENCES SDN(ent_num)
        ON UPDATE NO ACTION
        ON DELETE NO ACTION
)" );
  return;
}

sub LASTUPDATETable {
  my $self = shift;

  $self->{dbh}->do("drop table if exists LASTUPDATE");
  $self->{dbh}->do( "
                     CREATE TABLE LASTUPDATE  (
        LASTUPDATEDATETIME	TEXT
        )" );
  return;
}

sub search {

  my $self   = shift;
  my $table  = shift;
  my $string = shift;
  my $resultset;
  my $search = $self->{db}->resultset($table);

  for (@_) {

    my $result = $search->search_phonetic( { $_ => $string } );
    if ( defined $result ) {
      while ( my $record = $result->next ) {
        push @{$resultset},
          {
          entitynumber => $record->ent_num,
          entityhit    => $record->$_
          };
      }
    }
  }

  return $resultset;
}

sub searchAddress {
  my $self = shift;
  my $csz  = shift;
  my $ctry = shift;

  my $search = $self->{db}->resultset('Address');

  my $result = $search->search_phonetic( { CityStateProvincePostalCode => $csz, Country => $ctry } );

  my $resultset;

  if ( defined $result ) {
    while ( my $record = $result->next ) {
      push @{$resultset},
        {
        entitynumber => $record->ent_num,
        entityhit    => $self->{db}->resultset('Sdn')->search( ent_num => $record->ent_num )->first->SDN_Name
        };
    }

  }
  return $resultset;

}

1;
