#!/usr/bin/perl

use strict;
use warnings;
use FindBin qw( $Bin );
use File::Copy qw( move copy );
use File::Path qw( make_path remove_tree );
use lib "$Bin/../../lib";
use POSIX qw( strftime );
use Data::Dumper;
use Date::Calc qw( Days_in_Month Add_Delta_Days Date_to_Days );
use JSON::XS;
use File::Slurp qw( write_file read_file );
use DBI;
use File::Find qw( find );

use App::POS::Config;
use App::POS::Utils qw(
  send_mail execution_dates emails_validate random_sleep
  ftp_put file_modif_secs send_alert_to_ui
);
use App::POS::Machine::Server;

my $date_from = shift // "";
my $date_to = shift // "";
my $mode = shift // "";

if ($mode eq 'cron') {
  my $sleep = random_sleep([10 .. 60]);
  print STDERR "$0> Will sleep $sleep seconds...\n";
  sleep $sleep;
}

$date_from = undef if $date_from eq '';
$date_to = undef if $date_to eq '';

my $APP;

my $cfg = App::POS::Config->new( file => "/opt/pos/etc/config.ini" );
my $info = $cfg->load();

if (exists $info->{STATION_NUMBER} && $info->{STATION_NUMBER} ne '0') {
  die "Must be executed in server machine...\n";
}

my $server = App::POS::Machine::Server->new( config => $cfg );

if (-e "/opt/pos/common/addons/saft-pt/app.1.07") {
  $APP = "/opt/pos/common/addons/saft-pt/app.1.07";
}
elsif (-e "/opt/pos/common/addons/saft-pt/app.1.06") {
  $APP = "/opt/pos/common/addons/saft-pt/app.1.06";
}
elsif (-e "/opt/pos/common/addons/saft-pt/app.1.05") {
  $APP = "/opt/pos/common/addons/saft-pt/app.1.05";
}
elsif (-e "/opt/pos/common/addons/saft-pt/app.1.04") {
  $APP = "/opt/pos/common/addons/saft-pt/app.1.04";
}
elsif (-e "/opt/pos/common/addons/saft-pt/app.1.03") {
  $APP = "/opt/pos/common/addons/saft-pt/app.1.03";
}

if ($server->is_arm()) {
  if (-e "/opt/pos/common/addons/saft-pt/app.gb3") {
    $APP = "/opt/pos/common/addons/saft-pt/app.gb3"
  } elsif (-e "/opt/pos/common/addons/saft-pt/app.arm") {
    $APP = "/opt/pos/common/addons/saft-pt/app.arm";
  }
}

die "Can't find saft exporter...\n" unless $APP;

my $db = $server->database();

my $LANG = $info->{LANG} || "";
my $DOC_TYPE_SERIE = $info->{DOC_TYPE_SERIE} || "";
my $STATION_NUMBER = $info->{STATION_NUMBER} || 0;
my $SAFT_TaxRegistratioNumber = $info->{SAFT_TaxRegistratioNumber} || "";
my $SAFT_BusinessName = $info->{SAFT_BusinessName} || "";
my $STATION_NAME = $info->{STATION_NAME} || "";
my $SAFT_ZipXml = $info->{SAFT_ZipXml} || 0;
my $GENERATE_LOCALY = $info->{SAFT_MONTHLY_GENERATE_LOCALY} || "";
my $INTERNET_SEND_MONTHLY_SAFT = $info->{INTERNET_SEND_MONTHLY_SAFT} || "";
my $INTERNET_SEND_MAIL_HARDLOCK = $info->{INTERNET_SEND_MAIL_HARDLOCK} || "";
my $SAFT_MONTHLY_GENERATE_LOCALY_FTP_SERVER = $info->{SAFT_MONTHLY_GENERATE_LOCALY_FTP_SERVER} // "";
my $SAFT_MONTHLY_GENERATE_LOCALY_FTP_PORT = $info->{SAFT_MONTHLY_GENERATE_LOCALY_FTP_PORT} // "";
my $SAFT_MONTHLY_GENERATE_LOCALY_FTP_USER = $info->{SAFT_MONTHLY_GENERATE_LOCALY_FTP_USER} // "";
my $SAFT_MONTHLY_GENERATE_LOCALY_FTP_PASS = $info->{SAFT_MONTHLY_GENERATE_LOCALY_FTP_PASS} // "";
my $SAFT_MONTHLY_GENERATE_LOCALY_FTP_FOLDER = $info->{SAFT_MONTHLY_GENERATE_LOCALY_FTP_FOLDER} // "";

my $SAFT_MONTHLY_SEND_AUTO = $info->{SAFT_MONTHLY_SEND_AUTO} || "0";
my $SAFT_AT_LOGIN = $info->{SAFT_AT_LOGIN} || "";
my $SAFT_AT_PASSWD = $info->{SAFT_AT_PASSWD} || "";
my $SENT_TO_AT = 0;

#my $LICENSED=`echo 1 | netcat localhost 8088 | grep LICENSED`;
#$LICENSED //= "";
#die "$LANG is in demo... aborting...\n" unless $LICENSED;

# make sure we clean to save it from AT, if being used
unlink </opt/pos/at_saft_result.*>;

if ($INTERNET_SEND_MAIL_HARDLOCK) {
  my $MACHINE_ID=`$Bin/machine_id`;

  if ($MACHINE_ID ne $INTERNET_SEND_MAIL_HARDLOCK) {
    send_alert_to_ui('HARDLOCK ENVIO EMAIL ESTA ERRADO<BR>'.$INTERNET_SEND_MAIL_HARDLOCK);
    die "This machine has hardlock $MACHINE_ID and should have $INTERNET_SEND_MAIL_HARDLOCK, aborting...\n";
  }
}

my $control_file = "/tmp/.saft_monthly.running";

# removes control file if it's age is buffer than 60 minutes
my $secs = file_modif_secs($control_file);
unlink $control_file if $secs > 60*60;

if (-e "$control_file") {
  die "$control_file exists, aborting...\n";
}

open(F, "> $control_file");
print F "1\n";
close(F);


# a custom email was used to send the saft to
if (-e "/opt/pos/common/tmp/saft_custom_email.txt") {
  $INTERNET_SEND_MONTHLY_SAFT = read_file("/opt/pos/common/tmp/saft_custom_email.txt");
  $INTERNET_SEND_MONTHLY_SAFT =~ s/\n//g;
  $INTERNET_SEND_MONTHLY_SAFT =~ s/\r//g;
  unlink "/opt/pos/common/tmp/saft_custom_email.txt";
}

if ($GENERATE_LOCALY) {
  saft_monthly_local_folder($db, $GENERATE_LOCALY, $date_from, $date_to);
}

if ($INTERNET_SEND_MONTHLY_SAFT) {
  die "Invalid email(s) found - $INTERNET_SEND_MONTHLY_SAFT\n"
    unless emails_validate($INTERNET_SEND_MONTHLY_SAFT);

  saft_monthly_network($db, $date_from, $date_to);
}

unlink $control_file;

#
# Functions
#

sub saft_monthly_local_folder {
  my( $db, $folder, $date_from, $date_to ) = @_;

  #my $max_date = $db->select("SELECT MAX(business_date) AS date FROM sales_headers");
  my $max_date = $db->select("SELECT NOW()::date AS date");
  return unless scalar @$max_date;

  my( $from, $to ) = dates_guess($max_date->[0]->{date});

  if (defined $date_from && defined $date_to) {
    $from = $date_from;
    $to = $date_to;
  }

  print STDERR "saft_monthly_local_folder - $folder - $from - $to\n";

  my $from_dir = $from;
  my $to_dir = $to;
  $from_dir =~ s/\-/\//g;
  #$to_dir =~ s/\-/\//g;

  my $dir = "/opt/pos/common/data/execution_control/saft_monthly_local_folder/$from_dir/$to_dir";
  return if -d $dir;

  print STDERR "\tdebug 1 - $from - $to - $dir\n";

  make_path($folder) unless -d $folder;

  # creating this file will make saft file to allways be ziped
  write_file("/tmp/.zip_saft_file", 1);

  # generate saft for the previous month
  system("$APP $folder $from $to");

  my $exists = `ls $folder/*#$from#$to#* 2> /dev/null | head -1`;
  chop($exists);

  # file was generated, mark this date as done
  if ($exists) {
    # try to send saft file to at automatically
    if ($SAFT_MONTHLY_SEND_AUTO && $SAFT_AT_LOGIN && $SAFT_AT_PASSWD) {
      _send_to_at($SAFT_AT_LOGIN, $SAFT_AT_PASSWD, $from, $exists) unless $SENT_TO_AT;

      if (-e "/opt/pos/at_saft_result.xml") {
        my $cnt = read_file("/opt/pos/at_saft_result.xml");
        my $no_docs = 0;

        if ($cnt =~ /O ficheiro submetido não contém nenhum documento/) {
          $no_docs = 1;
        }

        my $biz_name = $SAFT_BusinessName;
        $biz_name =~ tr/ %-*\/,#"/________/;

        if ($cnt =~ /\<response code=\"200\"\>/ || $no_docs) {
          copy("/opt/pos/at_saft_result.xml", "$folder/at_success#$biz_name#$from#$to.xml");
          unlink "$folder/at_error#$biz_name#$from#$to.xml";
          make_path($dir);
        }
        else {
          copy("/opt/pos/at_saft_result.xml", "$folder/at_error#$biz_name#$from#$to.xml");
          print STDERR "ERROR SENDING TO AT...\n$cnt\n";
          remove_tree($dir);
        }
      }
    }
    else {
      make_path($dir);
    }
  }

  # put saft files in ftp
  if (
    $SAFT_MONTHLY_GENERATE_LOCALY_FTP_SERVER &&
    $SAFT_MONTHLY_GENERATE_LOCALY_FTP_PORT &&
    $SAFT_MONTHLY_GENERATE_LOCALY_FTP_USER &&
    $SAFT_MONTHLY_GENERATE_LOCALY_FTP_PASS &&
    $SAFT_MONTHLY_GENERATE_LOCALY_FTP_FOLDER
  ) {
    my $ftp_server = $SAFT_MONTHLY_GENERATE_LOCALY_FTP_SERVER;
    my $ftp_port = $SAFT_MONTHLY_GENERATE_LOCALY_FTP_PORT;
    my $ftp_login = $SAFT_MONTHLY_GENERATE_LOCALY_FTP_USER;
    my $ftp_pass = $SAFT_MONTHLY_GENERATE_LOCALY_FTP_PASS;
    my $ftp_folder = $SAFT_MONTHLY_GENERATE_LOCALY_FTP_FOLDER;

    find( sub {
      return unless -f $File::Find::name;

      my $st = 0;

      eval {
        $st = ftp_put($ftp_server, $ftp_port, $ftp_login, $ftp_pass, $ftp_folder, $File::Find::name);
      };

      if ($@) {
        print STDERR "ftp error - $@\n";
        return;
      }

      if ($st) {
        unlink $File::Find::name;
      }
    }, ( $folder ));
  }
}

sub saft_monthly_network {
  my( $db, $date_from, $date_to ) = @_;

  #my $max_date = $db->select("SELECT MAX(business_date) AS date FROM sales_headers");
  my $max_date = $db->select("SELECT NOW()::date AS date");
  return unless scalar @$max_date;

  my( $from, $to ) = dates_guess($max_date->[0]->{date});

  if (defined $date_from && defined $date_to) {
    $from = $date_from;
    $to = $date_to;
  }

  print STDERR "saft_monthly_network - $from - $to\n";

  my $has_network = `/opt/pos/common/bin/has_network`;

  # check network connection
  if ($has_network !~ /YES/i) {
    print STDERR "\tNo network connection...\n";
    return;
  }

  my $from_dir = $from;
  my $to_dir = $to;
  $from_dir =~ s/\-/\//g;
  #$to_dir =~ s/\-/\//g;

  my $dir = "/opt/pos/common/data/execution_control/saft_monthly_network/$from_dir/$to_dir";
  print STDERR "control_dir - $dir\n";
  return if -d $dir;

  my $folder = "/tmp/saft_to_network";
  make_path($folder) unless -d $folder;

  print STDERR "\tdebug 1 - $from - $to - $folder\n";

  # creating this file will make saft file to allways be ziped
  write_file("/tmp/.zip_saft_file", 1);

  # generate saft for the previous month
  system("$APP $folder $from $to");

  my $exists = `ls $folder/*#$from#$to#* 2> /dev/null | head -1`;

  # file was generated, mark this date as done
  if ($exists) {
    chop($exists);

    my $file_to_send = $exists;
    my $old_file_to_send = $file_to_send;
    my $biz_name = $SAFT_BusinessName;
    $biz_name =~ tr/ %-*\/,#"/________/;
    $file_to_send =~ s/\.xml$//;
    $file_to_send =~ s/\.xml\.zip$//;
    $file_to_send = $file_to_send.".xml";
    #$file_to_send = "$file_to_send.zip" if $SAFT_ZipXml;
    $file_to_send = "$file_to_send.zip";
    $file_to_send =~ tr/ /_/;

    move($exists, $file_to_send);

    eval {
      my $name = $STATION_NAME ? $STATION_NAME : $SAFT_BusinessName;

      my $stat = send_mail(
        to         => $INTERNET_SEND_MONTHLY_SAFT,
        subject    => "[$LANG SAF-T MONTHLY] $SAFT_TaxRegistratioNumber / $name / $from / $to",
        content    => "$LANG<br>====<p>$SAFT_BusinessName<p>SAF-T $from - $to<p>$SAFT_TaxRegistratioNumber<br>",
        attachment => [ $file_to_send ],
      );

      if ($stat) {
        # mark as processed
        make_path($dir);

        # try to send saft file to at automatically
        if ($SAFT_MONTHLY_SEND_AUTO && $SAFT_AT_LOGIN && $SAFT_AT_PASSWD) {
          _send_to_at($SAFT_AT_LOGIN, $SAFT_AT_PASSWD, $from, $file_to_send) unless $SENT_TO_AT;

          if (-e "/opt/pos/at_saft_result.xml") {
            my $cnt = read_file("/opt/pos/at_saft_result.xml");
            my $subject_prefix = "";
            my $no_docs = 0;

            if ($cnt =~ /O ficheiro submetido não contém nenhum documento/) {
              $no_docs = 1;
            }

            if ($cnt =~ /\<response code=\"200\"\>/ || $no_docs) {
              $subject_prefix = "SUCESSO ENVIO AT";
            }
            else {
              $subject_prefix = "ERRO ENVIO AT";
              remove_tree($dir);
            }

            copy("/opt/pos/at_saft_result.xml", "/opt/pos/at_saft_result#$biz_name#$from#$to.xml");

            my $stat2 = send_mail(
              to         => $INTERNET_SEND_MONTHLY_SAFT,
              subject    => "$subject_prefix / $SAFT_TaxRegistratioNumber / $name / $from / $to",
              content    => "$LANG<br>====<p>$SAFT_BusinessName<p>$name<p>$DOC_TYPE_SERIE<p>SAF-T $from - $to<p>$SAFT_TaxRegistratioNumber<p>$cnt",
              attachment => [ "/opt/pos/at_saft_result#$biz_name#$from#$to.xml" ],
            );
          }
        }
      }
    };

    if ($@) {
      print STDERR "ERROR in send_mail() - $@\n";
    }

    unlink $file_to_send;
  }

  remove_tree($folder);
}

sub dates_guess {
  my $date = shift;

  my( $y ) = $date =~ /^(\d\d\d\d)\-/;
  my( $m ) = $date =~ /^\d\d\d\d-(\d\d)\-/;

  if ($m eq '01') {
    $m = 12;
    $y--;
  }
  else {
    $m = $m*1;
    $m = sprintf("%02d", --$m);
  }

  my $days = Days_in_Month($y,$m);
  return ( "$y-$m-01", "$y-$m-$days" )
}

sub _send_to_at {
  my $login = shift;
  my $passwd = shift;
  my $date = shift;
  my $file = shift;

  my $dir = "/tmp/saft_extract_$$";
  make_path($dir);

  my $file_to_send = $file;

  # file is ziped
  if ($file =~ /\.zip$/i) {
    my $cmd = "unzip -o -q -d $dir \"$file\"";
    system($cmd);

    my $xml_file = <$dir/*.xml>;
    $xml_file //= "";
    $file_to_send = $xml_file;
  }

  # can't find file to send
  if (! $file_to_send) {
    remove_tree($dir);
    return 0;
  }

  print STDERR "SEND TO AT - $file_to_send - $date\n";

  my( $year, $month ) = $date =~ /^(\d\d\d\d)\-(\d\d)\-/;

  my $cmd = "/usr/bin/java -jar /opt/pos/common/addons/at_comms/FACTEMICLI-cmdClient.jar ".
    "-c /tmp -n \"$login\" -p \"$passwd\" -a $year -m $month -op enviar -i \"$file_to_send\" ".
    "-o /opt/pos/at_saft_result.xml > /opt/pos/at_saft_app.log"; # -t - to make tests

  print STDERR "WILL EXECUTE: $cmd\n";

  system($cmd);
  remove_tree($dir);

  my $downloaded_new_version = _check_at_version_error();

  if ($downloaded_new_version) {
    # we remove this file to avoid alerting the customer with an error
    # since we downloaded the new version
    unlink "/opt/pos/at_saft_result.xml";
    # if we downloaded a new version, it means we didnt send it to AT
    $SENT_TO_AT = 0;
  } else {
    $SENT_TO_AT = 1;
  }

  return 1;
}

sub _check_at_version_error {
  return unless -e "/opt/pos/at_saft_result.xml";
  my $cnt = read_file('/opt/pos/at_saft_result.xml');
  my $cnt2 = "";

  if (-e "/opt/pos/at_saft_app.log") {
    $cnt2 = read_file('/opt/pos/at_saft_app.log');
  }

  my( $version ) = $cnt =~ /obtenha a nova versão (.*?)\.<\/error/;
  $version ||= "";

  my( $version2 ) = $cnt2 =~ /do jar com a nova versão (.*?)\.<\/error/;
  $version2 ||= "";

  if (! $version && ! $version2) {
    print STDERR "VERSION IS OK...\n";
  }

  return unless $version || $version2;

  my $version_final = $version || $version2;

  print STDERR "NEED TO DOWNLOAD VERSION - $version_final\n";

  print STDERR "FACTEMICLI-cmdClient.jar has a new version - $version_final, will download...\n";
  unlink "/tmp/FACTEMICLI-cmdClient.jar" if -e "/tmp/FACTEMICLI-cmdClient.jar";

  my $url = 'https://faturas.portaldasfinancas.gov.pt/factemipf_static/java/FACTEMICLI-'.$version_final.'-cmdClient.jar';

  system("curl -s --fail \"$url\" --output /tmp/FACTEMICLI-cmdClient.jar");

  if ($? != 0) {
    print STDERR "ERROR DOWNLOADING - https://faturas.portaldasfinancas.gov.pt/factemipf_static/java/FACTEMICLI-".$version_final."-cmdClient.jar\n";
  } else {
    print STDERR "DOWNLOAD SUCCESS\n";
    move("/tmp/FACTEMICLI-cmdClient.jar", "/opt/pos/common/addons/at_comms/FACTEMICLI-cmdClient.jar");
    return 1;
  }

  return;
}

