<?php

namespace App\Console\Commands;

use App\CronJobStat;
use App\PendingSingleSms;
use App\Tools\CustomSmppClient;
use App\Tools\EmailAlert;
use App\Tools\GsmEncoder;
use App\Tools\ServiceTools;
use App\Tools\SmppAddress;
use App\Tools\SocketTransport;
use App\Tools\SMPP;
use App\Tools\SmsTools;
use App\User;
use App\UserJob;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Facades\Excel;

class SpeedySingleSms extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'qsingle_sms:start';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Send Single SMS at a speed of light!';

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

    public function handle()
    {
        //update & check cron job status for sending bulk sms
        $this->update_cron_job_status_running('SINGLE_SMS', 'RUNNING');
        $pot = env("SEND_SMS_API");
        $cdate = Carbon::now();
        $cron_status_running = true;
        try {
            $this->info(Carbon::now()->toDateTimeString() . '| Checking for sms...http://144.76.58.179/bulksms/mtn_api.php');
            $this->check_for_stop_flag();
            while (Carbon::now()->diffInSeconds($cdate) <= 55) {
                try {
                    DB::table('pending_single_sms')
                        ->where('sched_datetime', '<=', Carbon::now()->toDateTimeString())
                        ->select('id')
                        ->orderBy('id')->chunk(500, function ($jo) use (&$cron_status_running) {
                            $this->check_for_stop_flag();
                            $this->info('Fetched ' . count($jo) . ' pending sms');
                            try {
                                $this->check_for_stop_flag();

                                DB::beginTransaction();
                                $this->info(Carbon::now()->toDateTimeString() . '| Sending Single SMS...');

                                foreach ($jo as $j) {
                                    try {
                                        try {
                                            //check stop service flag

                                            $pendingSms = PendingSingleSms::find($j->id);
                                            $this->info('Processing ' . $pendingSms->msisdn);

                                            //computing
                                            $new_msg_text = preg_replace(array('/\r\n/'), array("\n"), trim($pendingSms->message));
                                            $no_chars = strlen($new_msg_text);//test
                                            $user = User::findOrFail($pendingSms->user_id);
                                            $credit = $user->credit;
                                            $no_pages = SmsTools::no_pages($no_chars);


                                            //prepare statement for logs
                                            $job_id = $user->username . '_' . date('YmdHis');
                                            $user_id = $pendingSms->user_id;
                                            $username = $user->username;
                                            $network = 'Unknown';
                                            $sender = $pendingSms->sender;
                                            $message = $new_msg_text;
                                            $sms_count = $no_pages;
                                            $submit_date = $pendingSms->sched_datetime;

                                            $created_at = Carbon::now()->toDateTimeString();
                                            $network_id = null;
                                            $sms_price = null;

                                            $msisdn = SmsTools::msisdn_prep(preg_replace('/\D/', '', $pendingSms->msisdn));
                                            $msisdn = trim($msisdn);
                                            $msisdn = SmsTools::format_gh_number($msisdn);

                                            $id = SmsTools::generate_new_sms_id();

                                            if (is_numeric($msisdn) && !empty($msisdn)) {

                                                //prepare sms to be sent
                                                if ($user->role->name === 'esme') {
                                                    $msgId = '';//$this->sendSmsForEsme($user->username, $user->pass, $sender, $msisdn, $message);
                                                } else {
                                                    $this->info('Processing job with id ' . $j->id . ' |user: ' . $username);
                                                    $request = array(
                                                        'username' => $user->username,
                                                        'password' => $user->pass,
                                                        'destination' => $msisdn,
                                                        'source' => $sender,
                                                        'message' => $message
                                                    );

                                                    $url = 'http://144.76.58.179/bulksms/mtn_api.php';

                                                    $ch = curl_init($url);
                                                    curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
                                                    curl_setopt($ch, CURLOPT_POST, 1);
                                                    curl_setopt($ch, CURLOPT_TIMEOUT, 1);
                                                    curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
                                                    $rep = curl_exec($ch);

                                                    $this->info(Carbon::now()->toDateTimeString() . '|' . $msisdn . ' message sent :' . $rep);
                                                    $pendingSms->delete();

                                                }

                                            } else {
                                                $this->info('MSISDN Not numeric!');
                                            }
                                        } catch (\Throwable $t) {
                                            $this->error($t->getMessage());
                                        }
                                    } catch (\PDOException $pdoex) {
                                        $this->info('Database Error: ' . $pdoex->getMessage());
                                        $this->update_cron_job_status_done('SINGLE_SMS', 'DONE');
                                        exit(3);
                                    } catch (\ErrorException $eex) {
                                        //commit transaction
                                        DB::commit();
                                        $this->update_cron_job_status_done('SINGLE_SMS', 'DONE');
                                        $this->info('Error: ' . $eex->getMessage());
                                        exit(3);
                                    } catch (\RuntimeException $re) {
                                        EmailAlert::insertError('SendSingleSms1', 'User: ' . $pendingSms->user->username . ', Msisdn: ' . $pendingSms->msisdn . ', Sender: ' . $pendingSms->sender . ', Error Message: ' . $re->getMessage(), $re->getLine());
                                    } catch (\Exception $e) {
                                        $this->error($e->getMessage());
                                        EmailAlert::insertError('SendSingleSms2', 'User: ' . $pendingSms->user->username . ', Msisdn: ' . $pendingSms->msisdn . ', Sender: ' . $pendingSms->sender . ', Error Message: ' . $e->getMessage(), $e->getLine());
                                    }

                                    $this->info('Task Complete!');
                                }

                                // Close connection
                                DB::commit();

                            } catch (\RuntimeException $re) {
                                EmailAlert::insertError('SendSingleSms', $re->getMessage(), $re->getLine());
                            } catch (\Exception $e) {
                                EmailAlert::insertError('SendSingleSms', $e->getMessage(), $e->getLine());
                            } catch (\Throwable $t) {
                                throw $t;
                            }
                        });
                } catch (\Throwable $t) {
                    throw $t;
                }
            }
        } catch (\Throwable $t) {
            if ($cron_status_running === true) {
                //update status of the running cron job
                $this->update_cron_job_status_done('SINGLE_SMS', 'DONE');
            }
            throw $t;
        }

        //update status of the running cron job
        if ($cron_status_running === true)
            $this->update_cron_job_status_done('SINGLE_SMS', 'DONE');
    }

    private function update_cron_job_status_done($name, $status)
    {
        $affected = DB::update(
            'update `cron_job_stats` set `child_process`=`child_process`-1 where `name` = ?',
            [$name]
        );
        if ((int)$affected !== 1) {
            EmailAlert::insertError($name, 'Database Error: Unable to update `cron_job_stats` table (' . $name . ': status->' . $status . ')');
            exit(1);
        }
        DB::update(
            'update `cron_job_stats` set `status`=? where `name` = ? AND `child_process`=?',
            [$status, $name, 0]
        );
    }

    private function update_cron_job_status_running($name, $status)
    {
        $now = Carbon::now()->toDateTimeString();
        $affected = DB::update(
            'update `cron_job_stats` set `last_run` = ?, `status`=?, `child_process`=`child_process`+1 where `name` = ?',
            [$now, $status, $name]
        );
        if ((int)$affected !== 1) {
            EmailAlert::insertError($name, 'Database Error: Unable to update `cron_job_stats` table (' . $name . ': status->' . $status . ')');
            exit(1);
        }
    }

    private function sendSmsForEsme($user, $pass, $sender, $msisdn, $message)
    {
        $this->info('Sending SMS via ' . $user . ' SMPP Account');
        $id = false;
        try {
            $transport = new SocketTransport(array(env('SMPP_HOST')), env('SMPP_PORT'));
            $transport->setRecvTimeout(60000);
            $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->bindTransmitter($user, $pass);

            CustomSmppClient::$csms_method = CustomSmppClient::CSMS_8BIT_UDH;
            CustomSmppClient::$sms_registered_delivery_flag = SMPP::REG_DELIVERY_SMSC_BOTH;

            $encodedMessage = GsmEncoder::utf8_to_gsm0338($message);
            $from = new SmppAddress($sender, SMPP::TON_ALPHANUMERIC);
            $to = new SmppAddress($msisdn, SMPP::TON_INTERNATIONAL, SMPP::NPI_E164);

            // Send
            $id = $smpp->sendSMS($from, $to, $encodedMessage);
            $smpp->close();
        } catch (\RuntimeException $re) {
            $this->error($re->getMessage());
            EmailAlert::insertError('SendSingleSms', 'User:' . $user . ', Sender:' . $sender . ', Msisdn:' . $msisdn . ', Message:' . $message . ', ErrorMsg:' . $re->getMessage(), $re->getLine());
            return $id;
        } catch (\Exception $e) {
            $this->error($e->getMessage());
            EmailAlert::insertError('SendSingleSms', 'User:' . $user . ', Sender:' . $sender . ', Msisdn:' . $msisdn . ', Message:' . $message . ', ErrorMsg:' . $e->getMessage(), $e->getLine());
            return $id;
        }
        return $id;
    }

    private function check_for_stop_flag()
    {
        //check stop service flag
        if (!ServiceTools::can_run_service()) {
            $this->info('Service Stopped By User!');
            $this->update_cron_job_status_done('SINGLE_SMS', 'DONE');
            exit(1);
        }
    }
}
