#!/usr/bin/perl

use strict;
use warnings;
use FindBin qw( $Bin );
use lib "$Bin/../../lib";
use Encode qw( encode decode );
use JSON::XS qw( decode_json encode_json );
use HTTP::Request::Common qw( POST PUT GET );
use Data::Dumper;
use LWP::UserAgent;
use Time::HiRes qw( sleep );
use Date::Calc qw( Date_to_Time Delta_Days Add_Delta_Days Today );
use Time::Piece;
use DateTime;
use File::Slurp qw( write_file );
use File::Copy qw( copy move );
use IO::Socket::Timeout;

use App::POS::Utils qw( log );
use App::POS::Config;
use App::POS::Machine::Station;
use App::POS::Machine::Server;

use constant VERSION => '1.0.0';

my $orderId = shift || '';
my $flagKey = shift || '';

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

$info->{AIRMENU_KEY} ||= '';
$info->{AIRMENU_LOGIN} ||= '';
$info->{AIRMENU_PASSWD} ||= '';
$info->{AIRMENU_ENTERPRISE_ID} ||= 0;
$info->{AIRMENU_AREA} ||= 0;

die "Missing AIRMENU_KEY or AIRMENU_LOGIN or AIRMENU_PASSWD or AIRMENU_ENTERPRISE_ID...\n"
  unless $info->{AIRMENU_KEY} && $info->{AIRMENU_LOGIN} && $info->{AIRMENU_PASSWD} &&
  $info->{AIRMENU_ENTERPRISE_ID};

my $airmenu_key = $info->{AIRMENU_KEY};
my $airmenu_user = $info->{AIRMENU_LOGIN};
my $airmenu_pass = $info->{AIRMENU_PASSWD};
my $airmenu_enterprise = $info->{AIRMENU_ENTERPRISE_ID};

my $machine = undef;

if (exists $info->{STATION_NUMBER} && $info->{STATION_NUMBER} > 0) {
  $machine = App::POS::Machine::Station->new( config => $cfg );
}
else {
  $machine = App::POS::Machine::Server->new( config => $cfg );
}

my $data = {};

# ###################################################################
# AirMenu - Authenticate
# ###################################################################

$data = {
  ACTION   => 'Authenticate',
  VERSION  => VERSION,
  KEY      => $airmenu_key,
  DATA     => encode_json({
    username => $airmenu_user,
    password => $airmenu_pass,
  }),
};

my $ref = _post_form('http://www.airmenu.com/AirMenuAPI', $data);
$ref->{status} ||= "";
$ref->{sessionId} ||= "";

print STDERR Dumper($ref);

if ($ref->{status} eq 'ERROR') {
  die 'AUTH ERROR - ' . $ref->{description}."\n";
}

if ($ref->{status} ne 'SUCCESS') {
  print STDERR Dumper($ref);
  die "Error in AirMenu auth...\n";
}

my $sessionId = $ref->{sessionId};
print STDERR "sessionId: $sessionId\n";

#
# we've received and order ID and an order state to change it
#
if ($orderId && $flagKey) {
  # {"sessionId": "dIT7tfkVargggdzF3QNWcA","enterpriseId":"578557857857866","operatorEmail":"username@gmail.com","executorEmail":"username@gmail.com","flagKey":"ACCEPT","orderId":"1931546"}
  my $ref = _post_form('http://www.airmenu.com/AirMenuAPI', {
    ACTION   => 'ActivateFlag',
    VERSION  => VERSION,
    KEY      => $airmenu_key,
    DATA     => encode_json({
      sessionId => $sessionId,
      enterpriseId => $airmenu_enterprise,
      operatorEmail => $airmenu_user,
      executorEmail => $airmenu_user,
      flagKey => $flagKey,
      orderId => $orderId,
    }),
  });

  $ref ||= {};
  log($machine->server_dir_data() . "/airmeny.log", "order_change_state#$orderId#$flagKey#".encode_json($ref), 1);
  print STDERR Dumper($ref);
  exit;
}


# ###################################################################
# AirMenu - GetEnterprisePosIds
# ###################################################################

$data = {
  ACTION   => 'GetEnterprisePosIds',
  VERSION  => VERSION,
  KEY      => $airmenu_key,
  DATA     => encode_json({
    sessionId    => $sessionId,
    enterpriseId => $airmenu_enterprise,
  }),
};

$ref = _post_form('http://www.airmenu.com/AirMenuAPI', $data);
print STDERR "GetEnterprisePosIds\n";
print STDERR Dumper($ref);

$ref->{posIds} ||= [];
print STDERR "##### posIds:\n";
print STDERR Dumper($ref->{posIds});
die "Can't find posIds..." unless scalar @{$ref->{posIds}};
my $posId = $ref->{posIds}->[0];

# ###################################################################
# AirMenu - GetNotificationAccess
# ###################################################################

$data = {
  ACTION   => 'GetNotificationAccess',
  VERSION  => VERSION,
  KEY      => $airmenu_key,
  DATA     => encode_json({
    sessionId    => $sessionId,
    enterpriseId => $airmenu_enterprise,
    posId        => $posId,
    # asExecutor => 1|0
  }),
};

$ref = _post_form('http://www.airmenu.com/AirMenuAPI', $data);
#print STDERR "GetNotificationAccess (posId: $posId)\n";
#print STDERR Dumper($ref);

=pod
$VAR1 = {
          'status' => 'SUCCESS',
          'version' => '1.0.0',
          'action' => 'GetNotificationAccess',
          'token' => '1696417870019#API#16989948359086654',
          'port' => '80',
          'statusCode' => 1,
          'host' => 'http://18.170.31.255/'
        };
=cut

my( $host ) = $ref->{host} =~ /^http\:\/\/(.*?)\//;
my $port = $ref->{port};
my $token = $ref->{token};

print STDERR "Will connect to host: $host, port: $port, send token: $token\n";

my $socket = IO::Socket::INET->new(
  PeerHost => $host,
  PeerPort => $port,
  Proto    => 'tcp',
  Timeout  => 3,
) or die "Error: $!\n";

IO::Socket::Timeout->enable_timeouts_on($socket);

# $socket->timeout(10);

$socket->read_timeout(10);
$socket->write_timeout(10);

print $socket "$token\n";

log($machine->server_dir_data() . "/airmeny.log", "will start reading from socket...", 1);

while (1) {
  my $response;
  $socket->recv($response, 1024); # Adjust the buffer size as needed
  $response ||= "";

  if (!$response) {
    if (!$socket->connected) {
      log($machine->server_dir_data() . "/airmeny.log", "socket not connected... will exit...", 1);
      print STDERR "SOCKET NOT CONNECTED, WILL EXIT...\n";
      exit;
    }
    print STDERR "#### ".$socket->connected."\n";
    sleep 0.5;
    next;
  }

  my @parts = split(/\0/, $response);

  print STDERR "### parts:\n";
  print STDERR Dumper(\@parts);

  foreach my $p (@parts) {
    if ($p eq 'SUCCESS') {
      print "Received from server: $response\n";
    }
    elsif ($p eq 'ApiAction_GetOrders') {
      my $orderIds = ApiAction_GetOrders(
        $airmenu_key,
        $sessionId,
        $airmenu_enterprise,
        $posId,
      );

      print STDERR "### orderIds:\n";
      print STDERR Dumper($orderIds);

      if (scalar @$orderIds) {
        # ###################################################################
        # AirMenu - GetOrders
        # ###################################################################

        log($machine->server_dir_data() . "/airmeny.log", "will get order " . $orderIds->[0], 1);

        $data = {
          ACTION   => 'GetOrders',
          VERSION  => VERSION,
          KEY      => $airmenu_key,
          DATA     => encode_json({
            sessionId    => $sessionId,
            enterpriseId => $airmenu_enterprise,
            orderId      => $orderIds->[0],
          }),
        };

        $ref = _post_form('http://www.airmenu.com/AirMenuAPI', $data);
        #print STDERR "GetOrders (orderId: #$orderIds->[0]#)\n";
        print STDERR Dumper($ref);

        $ref->{orders} ||= {};

        my @k = keys %{$ref->{orders}};

        $ref->{orders}->{$k[0]} ||= [];

        if (scalar @{$ref->{orders}->{$k[0]}}) {
          my $order = _airmenu_decode_order($ref->{orders}->{$k[0]});

          print STDERR Dumper($order);

          my $nif               = $order->{extra_info}->{AM_TAX_PAYER_NUMBER} || '';
          my $order_type        = $order->{extra_info}->{AM_ORDER_TYPE} || '';
          my $note              = $order->{extra_info}->{AM_NOTE} || '';
          my $fee               = $order->{extra_info}->{AM_FEE} || '';
          my $provider_name     = $order->{extra_info}->{AM_PROVIDER_NAME} || '';
          my $provider_order_id = $order->{extra_info}->{AM_PROVIDER_ORDER_ID} || '';
          # my $order_id          = $order->{orderId};

          $nif               =~ s/#//g if $nif;
          $order_type        =~ s/#//g if $order_type;
          $note              =~ s/#//g if $note;
          $fee               =~ s/#//g if $fee;
          $provider_name     =~ s/#//g if $provider_name;
          $provider_order_id =~ s/#//g if $provider_order_id;

          my $notes = "";
          $notes .= "TYPE: $order_type§" if $order_type;
          $notes .= "NOTE: $note§" if $note;
          $notes .= "FEE : $fee§" if $fee;

          if ($provider_order_id) {
            # $notes .= sprintf("%-15s", $provider_name) . ": " . $provider_order_id . "§";
            $notes .= "CODE $provider_name: " . $provider_order_id . "§";
          }

          my $cnt = "0#airmenu#airmenu#$nif##0#$info->{AIRMENU_AREA}#$notes#0#0\n";

=pod
$VAR1 = {
          'pmethod' => 'Money',
          'products' => [
                          {
                            'extra_info' => {
                                              'AM_NOTE' => 'prod obs'
                                            },
                            'plu' => '',
                            'price' => '1.5',
                            'qty' => 1,
                            'type' => 'item',
                            'title' => 'Seven Up'
                          }
                        ],
          'extra_info' => {
                            'nif' => '',
                            'AM_ORDER_TYPE' => 'order type',
                            'AM_NOTE' => 'order obs',
                            'AM_FEE' => 'fee',
                            'AM_TAX_PAYER_NUMBER' => '507281420',
                            'AM_PROVIDER_NAME' => 'platform',
                            'AM_PROVIDER_ORDER_ID' => 'code platform'
                          },
          'nif' => ''
        };
=cut

          my $products = $order->{products} || [];

          foreach my $p (@$products) {
            my $type = $p->{type} || '';
            if ($type ne 'item') {
              print STDERR "UNKOWN PRODUCT TYPE - $type - will ignore...\n";
              next;
            }
            my $note = $p->{extra_info}->{AM_NOTE} || '';
            if ($note) {
              $note =~ s/#//g;
              $note =~ s/;//g;
              $note =~ s/\r//g;
              $note =~ s/\n/;/g;
            }
            my $plu = $p->{plu} || '';
            # $plu = 18;
            my $price = $p->{price} || 0;
            my $qty = $p->{qty} || 0;
            my $title = $p->{title} || '';

            if ($plu eq '') {
              _send_msg_to_ui('PRODUTO SEM PLU - ' . $title);
              next;
            }

            if (! $title) {
              _send_msg_to_ui('PRODUTO SEM NOME, IGNORAR...');
              next;
            }

            # $cnt .= "airmenu#$plu#$title#$qty#$price#-1#0#$note\n";
            $cnt .= "airmenu#$plu#$qty#$price#-1#0#$note\n";
          }

          $cnt .= "extra_data#AM_TAX_PAYER_NUMBER#$nif\n";
          $cnt .= "extra_data#AM_ORDER_TYPE#$order_type\n";
          $cnt .= "extra_data#AM_NOTE#$note\n";
          $cnt .= "extra_data#AM_FEE#$fee\n";
          $cnt .= "extra_data#AM_PROVIDER_NAME#$provider_name\n";
          $cnt .= "extra_data#AM_PROVIDER_ORDER_ID#$provider_order_id\n";
          $cnt .= "extra_data#AM_ORDER_ID#$order->{orderId}\n";

          write_file("/tmp/$$.tmp", $cnt);
          move("/tmp/$$.tmp", "/opt/pos/server/data/tmp/check_import#$$#0.txt");

          _send_msg_to_ui('NOVO PEDIDO PLATAFORMAS');

          print STDERR "############################\n";
          print STDERR "$cnt\n";

          # cp ~/Desktop/check_sample.txt /opt/pos/server/data/tmp/check_import#1.txt
          #13#airmenu#V2#507281420#ULOAN IT, LDA#1#1#obs doc2§miguel§#1#1
          #airmenu##COCA COLA#1.00#2#13#0#obs1;obs2;
          #airmenu##7UP#1.00#2#13#0#
          #airmenu##Compal#1.00#2#13#0#
          #50#DINHEIRO
          #print STDERR "###################### _airmenu_decode_order:\n";
          #print STDERR Dumper($order);
        }
      }
    }
    else {
      print STDERR "Don't know what to do with: #$response#\n";
    }
  }

  sleep 0.1;
}

# ###################################################################
# AirMenu - ApiAction_GetOrders
# ###################################################################

sub ApiAction_GetOrders {
  my( $airmenu_key, $sessionId, $airmenu_enterprise, $posId ) = @_;
  print STDERR "ApiAction_GetOrders, airmenu_enterprise: $airmenu_enterprise, posId: $posId\n";

=pod
  # ###################################################################
  # AirMenu - GetOrders
  # ###################################################################

  $data = {
    ACTION   => 'GetOrders',
    VERSION  => VERSION,
    KEY      => $airmenu_key,
    DATA     => encode_json({
      sessionId    => $sessionId,
      enterpriseId => $airmenu_enterprise,
      posId => $posId,
    }),
  };

  $ref = _post_form('http://www.airmenu.com/AirMenuAPI', $data);

  print STDERR "GetOrders\n";
  print STDERR Dumper($ref);
=cut

  # ###################################################################
  # AirMenu - GetOrderIds
  # ###################################################################

  my $dt = DateTime->now;
  $dt->subtract(seconds => 2);

  my $cur_date = $dt->strftime('%Y-%m-%d');
  my $from = $dt->strftime('%Y-%m-%d %H:%M:%S');
  # my $from = $dt->strftime('%Y-%m-%d %H:%M:00');
  my $to = _date_based_on_closing_hour($dt->strftime('%Y-%m-%d %H:%M:%S'), "05:00:00");

  print STDERR "################### from: $from, to: $to\n";

  if ($to eq $cur_date) {
    $to .= ' 23:59:59';
  } else {
    # $from = $cur_date . " 00:00:00";
    $to = $cur_date . ' 05:00:00';
  }

  print STDERR "from: $from, to: $to\n";

  $data = {
    ACTION   => 'GetOrderIds',
    VERSION  => VERSION,
    KEY      => $airmenu_key,
    DATA     => encode_json({
      sessionId    => $sessionId,
      enterpriseId => $airmenu_enterprise,
      startDate    => _airmenu_date($from),
      endDate      => _airmenu_date($to),
    }),
  };

  $ref = _post_form('http://www.airmenu.com/AirMenuAPI', $data);

  print STDERR "GetOrderIds\n";
  print STDERR Dumper($ref);

  $ref->{orderIds} ||= [];
  return $ref->{orderIds};
}

#
# functions
#

sub _date_based_on_closing_hour {
  my $sale_date = shift;
  my $closing   = shift;

  $closing = $closing->[0] if ref($closing);

  my( $y, $m, $d, $h, $mi, $s ) = $sale_date =~ /^(\d+)\-(\d+)\-(\d+) (\d+):(\d+):(\d+)$/;

  my $close = "$y-$m-$d $closing";
  my( $y2, $m2, $d2, $h2, $mi2, $s2 ) = $close =~ /^(\d+)\-(\d+)\-(\d+) (\d+):(\d+):(\d+)$/;

  my $sale_time  = &Date_to_Time( $y, $m, $d, $h, $mi, $s );
  my $close_time = &Date_to_Time( $y2, $m2, $d2, $h2, $mi2, $s2 );

  my $diff = $sale_time - $close_time;

  if ($h2 >= 12) {
    if ($diff > 0) {
      $sale_date =~ s/\s+.*?$//;
      return $sale_date;
    } else {
      #my( $year, $month, $day ) = $sale_date =~ /^(\d+)\-(\d+)\-(\d+)/;
      my ($fy, $fm, $fd) = Add_Delta_Days($y, $m, $d, -1);
      return "$fy-".sprintf("%02d", $fm)."-".sprintf("%02d", $fd);
    }
  } else {
    if ($diff > 0) {
      $sale_date =~ s/\s+.*?$//;
      return $sale_date;
    } else {
      my ($fy, $fm, $fd) = Add_Delta_Days($y, $m, $d, -1);
      return "$fy-".sprintf("%02d", $fm)."-".sprintf("%02d", $fd);
    }
  }

  $sale_date =~ s/\s+.*?$//;
  return $sale_date;
}

sub _airmenu_decode_order {
  my $o = shift;

  my %valid_menu_relation = (
    # family => 1,
    item => 1,
    # complement => 1,
    complementItem => 1,
  );

  my $nif = "";
  my $pmethod = "";
  my $order_id = "";

  my @products = ();

  foreach my $child0 (@$o) {
    $child0->{childs} ||= [];

    $order_id = $child0->{orderId};

    $child0->{extraInfo}->[0]->{nif} ||= "";
    $nif = $child0->{extraInfo}->[0]->{nif};

    $child0->{paymentMethod} ||= "";
    $pmethod = $child0->{paymentMethod};

    if (exists $valid_menu_relation{$child0->{menuRelation}}) {
      push @products, {
        type => $child0->{menuRelation},
        plu => $child0->{plu},
        title => $child0->{title},
        price => $child0->{price},
        qty => $child0->{count} || 0,
        extra_info => exists $child0->{extraInfo} && scalar @{$child0->{extraInfo}} ? $child0->{extraInfo}->[0] : {},
      };
    }

    foreach my $child1 (@{$child0->{childs}}) {
      $child1->{childs} ||= [];

      if (exists $valid_menu_relation{$child1->{menuRelation}}) {
        push @products, {
          type => $child1->{menuRelation},
          plu => $child1->{plu},
          title => $child1->{title},
          price => $child1->{price},
          qty => $child1->{count} || 0,
          extra_info => exists $child1->{extraInfo} && scalar @{$child1->{extraInfo}} ? $child1->{extraInfo}->[0] : {},
        };
      }

      foreach my $child2 (@{$child1->{childs}}) {
        my $type = $child2->{menuRelation};
        $child2->{childs} ||= [];

        if (exists $valid_menu_relation{$child2->{menuRelation}}) {
          push @products, {
            type => $child2->{menuRelation},
            plu => $child2->{plu},
            title => $child2->{title},
            price => $child2->{price},
            qty => $child2->{count} || 0,
            extra_info => exists $child2->{extraInfo} && scalar @{$child2->{extraInfo}} ? $child2->{extraInfo}->[0] : {},
          };
        }

        foreach my $child3 (@{$child2->{childs}}) {
          my $type = $child3->{menuRelation};
          $child3->{childs} ||= [];

          if (exists $valid_menu_relation{$child3->{menuRelation}}) {
            push @products, {
              type => $child3->{menuRelation},
              plu => $child3->{plu},
              title => $child3->{title},
              price => $child3->{price},
              qty => $child3->{count} || 0,
              extra_info => exists $child3->{extraInfo} && scalar @{$child3->{extraInfo}} ? $child3->{extraInfo}->[0] : {},
            };
          }

          foreach my $child4 (@{$child3->{childs}}) {
            my $type = $child4->{menuRelation};
            $child4->{childs} ||= [];

            if (exists $valid_menu_relation{$child4->{menuRelation}}) {
              push @products, {
                type => $child4->{menuRelation},
                plu => $child4->{plu},
                title => $child4->{title},
                price => $child4->{price},
                qty => $child4->{count} || 0,
                extra_info => exists $child4->{extraInfo} && scalar @{$child4->{extraInfo}} ? $child4->{extraInfo}->[0] : {},
              };
            }

            foreach my $child5 (@{$child4->{childs}}) {
              my $type = $child5->{menuRelation};
              $child5->{childs} ||= [];

              if (exists $valid_menu_relation{$child5->{menuRelation}}) {
                push @products, {
                  type => $child5->{menuRelation},
                  plu => $child5->{plu},
                  title => $child5->{title},
                  price => $child5->{price},
                  qty => $child5->{count} || 0,
                  extra_info => exists $child5->{extraInfo} && scalar @{$child5->{extraInfo}} ? $child5->{extraInfo}->[0] : {},
                };
              }
            }
          }
        }
      }
    }
  }

  #print STDERR Dumper(\@products);
  #print STDERR "### NIF: $nif\n";
  #print STDERR "### PMETHOD: $pmethod\n";

  my $ref = {
    orderId => $order_id,
    nif => $nif,
    pmethod => $pmethod,
    products => \@products,
    extra_info => exists $o->[0]->{extraInfo} && scalar @{ $o->[0]->{extraInfo}} ?  $o->[0]->{extraInfo}->[0] : {},
  };

  return $ref;
}

sub _airmenu_date {
  my $dstr = shift;
  my $time = Time::Piece->strptime($dstr, '%Y-%m-%d %H:%M:%S');
  # subtract 1 seconds to date from date
  $time -= 2;
  return $time->epoch * 1000;
}

sub _post_json {
  my $server = shift;
  my $url = shift;
  my $dbname = shift;
  my $api_key = shift;
  my $ref = shift;

  my $ua = LWP::UserAgent->new();
  #$ua->timeout(5);

  my $req = HTTP::Request->new('POST' => $server.$url);

  $req->header('content-type' => 'application/json');
  $req->header('db' => $dbname);
  $req->header('token' => $api_key);

  $req->content(encode_json($ref));

  my $r = $ua->request($req);

  if ($r->code() != 200) {
    print STDERR "Error posting... " . $r->content()."\n";
    return undef;
  }
  else {
    return decode_json($r->content());
  }
}

sub _post_form {
  my $url = shift;
  my $data = shift || {};
  my $timeout = shift || 10;
  return unless defined $url;

  $data ||= {};

  my $ua = LWP::UserAgent->new();
  $ua->timeout($timeout);

  my $r = $ua->request(POST $url, [
    %$data
  ]);

  return undef if $r->code != 200;

  my $cnt = $r->content();
  #print $cnt."\n";
  $cnt =~ s/RESULT=//;
  return decode_json(encode("utf8", $cnt));
}

sub _send_msg_to_ui {
  my $msg = shift || "";
  return unless $msg;

  print STDERR ">> $msg\n";

  my $sock = IO::Socket::INET->new(
    PeerAddr => '127.0.0.1',
    PeerPort => '8088',
    Proto => 'tcp',
    Timeout => 5,
  );

  if (defined $sock && $sock) {
    print $sock "#ALERT#$msg\n" if defined $sock && $sock;
    $sock->close();
  }
  else {
    print STDERR "_send_barcode_to_ui - error connecting to port 8088\n";
  }

}

