<?php

namespace App\Console\Commands;

use App\CronJobStat;
use App\Log;
use App\Tools\CustomSmppClient;
use App\Tools\EmailAlert;
use App\Tools\ServiceTools;
use App\Tools\SmppDeliveryReceipt;
use App\Tools\SmppException;
use App\Tools\SocketTransport;
use Carbon\Carbon;
use Doctrine\DBAL\Driver\PDOException;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;

class DeliveryReceiver extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'csms:receive {instance}';//signature

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Receive Delivery Report';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $instance = $this->argument('instance');
        if(env('RECEIVE_DLR', false)) {
//            $this->info('Starting Receiver '.$instance.' ...');
            CronJobStat::where('name', 'DLR_RECEIVER')->update([
                'last_run'=>Carbon::now(),
                'status' => 'RUNNING',
                'child_process'=> 1
            ]);
            // Construct transport and client
            $smpp = null;
            try {
                $transport = new SocketTransport(array(env('SMPP_HOST')), env('SMPP_PORT'));
                $transport->setRecvTimeout(5000); // for this example wait up to 5 seconds for data
                $smpp = new CustomSmppClient($transport);

                // Activate binary hex-output of server interaction
                $smpp->debug = env('APP_DEBUG');
                $transport->debug = env('APP_DEBUG');

                // Open the connection
                $transport->open();
                $smpp->bindReceiver(env('SMPP_USER'), env('SMPP_PASSWORD'));
                $now = Carbon::now();

                // Read SMS and output
                $sms = null;
                while (Carbon::now()->diffInSeconds($now)<=54) {
                    try {
                        //check stop service flag
                        if(!ServiceTools::can_run_service()) {
                            $this->close_smpp_connection($smpp);
                            $this->info('Service Stopped By User!');
                            exit(1);
                        }

                        $sms = null;
                        $sms = $smpp->readSMS();

                        //process here
                        if ($sms instanceof SmppDeliveryReceipt) {
                            $msgId = $sms->id;

                            $log = Log::where('msgid', $msgId)->first();

                            $log_received = DB::table('log_received')->where('msgid', $msgId)->orderBy('created_at', 'desc')->first();

                            //update db
                            $submitDate = Carbon::parse(gmdate('Y-m-d H:i:s', $sms->submitDate));
                            $delivDate = Carbon::parse(gmdate('Y-m-d H:i:s', $sms->doneDate));
                            $diff = $delivDate->diffInSeconds($submitDate);
                            if (empty($log_received)) {
                                //insert
                                DB::table('log_received')->insert(
                                    [
                                        'id' => !empty($log) ? $log->id : '',
                                        'msgid' => $msgId,
                                        'status' => $sms->stat,
                                        'delivery_date' => $delivDate->toDateTimeString(),
                                        'sent_date' => $submitDate->toDateTimeString(),
                                        'delivery_time' => $diff,
                                        'error_code' => $sms->err,
                                        'created_at' => Carbon::now()->toDateTimeString()
                                    ]
                                );
                            } else {
                                //update
                                DB::table('log_received')
                                    ->where('msgid', $msgId)
                                    ->update(
                                        [
                                            'id' => !empty($log) ? $log->id : '',
                                            'status' => $sms->stat,
                                            'delivery_date' => $delivDate->toDateTimeString(),
                                            'sent_date' => $submitDate->toDateTimeString(),
                                            'delivery_time' => $diff,
                                            'error_code' => $sms->err,
                                            'updated_at' => Carbon::now()->toDateTimeString()
                                        ]
                                    );
                            }
//                            $this->info(var_export($sms, true));
                            $this->info("[$instance] ".Carbon::now()->toDateTimeString() . '| source:' . $sms->source->value .
                                ' | dest:' . $sms->destination->value . ' | stat:' . $sms->stat . ' | id:' . $sms->id);

                        }
                    } catch (PDOException $pex) {
                        $this->error($pex->getMessage());
                        EmailAlert::insertError('DeliveryReceiver', (!empty($sms) ? ('From:'.$sms->destination->value.', To:'.$sms->source->value.', MsgId:'.$sms->id):''). ' - '.$pex->getCode().': '. $pex->getMessage(), $pex->getLine());
                        $this->close_smpp_connection($smpp);
                        exit(2);
                    } catch (\Throwable $ex) {
                        $this->error($ex->getMessage());
                        if($this->should_send_error_alert($ex)) {
                            EmailAlert::insertError('DeliveryReceiver', (!empty($sms) ? ('From:'.$sms->destination->value.', To:'.$sms->source->value.', MsgId:'.$sms->id):''). ' - '.$ex->getCode().': '. $ex->getMessage(), $ex->getLine());
                        }
                    }
                }

                // Close connection
                $this->close_smpp_connection($smpp);
            } catch (\Throwable $e) {
                $this->close_smpp_connection($smpp);
                $this->error($e->getMessage());
                if($this->should_send_error_alert($e)) {
                    EmailAlert::insertError('DeliveryReceiver', $e->getMessage(), $e->getLine());
                }
            }
            CronJobStat::where('name', 'DLR_RECEIVER')->update([
                'status' => 'DONE',
                'child_process'=> 0
            ]);
//            $this->info('Stopping Receiver '.$instance.' ...');
        }
    }

    private function should_send_error_alert(\Throwable $e) {
        return strstr($e->getMessage(), 'Could not parse delivery receipt:')===false &&
            strstr($e->getMessage(), 'unpack(): Type N: not enough input,')===false;
    }

    private function close_smpp_connection($smpp) {
        if(!empty($smpp)) {
            try {
                $smpp->close();
            } catch (\RuntimeException $se){}
        }
    }
}
