<?php

namespace App\Jobs;

use App\Exceptions\ErrorReporting;
use App\Tools\ServiceTools;
use App\Tools\Sms;
use App\Tools\SmsTools;
use App\UserJob;
use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Maatwebsite\Excel\Facades\Excel;

class SendPersoBulkSms implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    protected $userJob;
    protected $userJobId;
    private $log_path = '';

    /**
     * Create a new job instance.
     *
     * @param $userJobId
     */
    public function __construct($userJobId)
    {
        $this->userJobId = $userJobId;
        $this->log_path = storage_path('sms_log' . DIRECTORY_SEPARATOR . 'speedy_sms_perso_bulk.log');
    }

    public static function update_msg($headers, $rowData, $msg) {
        $retValue = $msg;
        for($i=0; $i< count($headers); $i++) {
            $header = $headers[$i];
            $value = isset($rowData[$i]) && !empty($rowData[$i]) ? $rowData[$i] : '';
            $retValue = str_ireplace('['.$header.']', $value, $retValue);
        }
        return $retValue;
    }

    private function info($text) {
        if(env('APP_ENV')=='local') {
            Log::info('INFO '.date('d-m-Y H:i:s').' || '.$text);
        } else
            \swoole_async_writefile($this->log_path, 'INFO '.date('d-m-Y H:i:s').' || '.$text.PHP_EOL, null, FILE_APPEND);
    }

    private function warn($text) {
        if(env('APP_ENV')=='local') {
            Log::info('WARN '.date('d-m-Y H:i:s').' || '.$text);
        } else
            \swoole_async_writefile($this->log_path, 'WARN '.date('d-m-Y H:i:s').' || '. $text.PHP_EOL, null, FILE_APPEND);
    }

    private function error($text) {
        if(env('APP_ENV')=='local') {
            Log::error('ERROR '.date('d-m-Y H:i:s').' || '.$text);
        } else
            \swoole_async_writefile($this->log_path, 'ERROR '.date('d-m-Y H:i:s').' || '. $text.PHP_EOL, null, FILE_APPEND);
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        $this->userJob = UserJob::find($this->userJobId);
        $job = &$this->userJob;
        if(empty($job)) {
            $this->error('Job Not Found!');
            return;
        }
        //check for esme users
        $tmp = DB::select('select r.name as role_name from users u left join roles r on u.role_id=r.id where u.id=?',[$job->user_id]);
        if(empty($tmp) || empty($tmp[0]) || $tmp[0]->role_name=='esme') {
            $this->error('Only normal users are allowed to send blast!');
            return;
        }
        if($job->status !== 'starting') {
            $this->error('Job '.$job->name.' has already started!');
            return;
        }
        $job->status = 'started';
        $job->task_started_date = Carbon::now()->toDateTimeString();
        $job->task_completed_date = null;
        $job->save();
        $path = $job->file_path;

        $this->info(Carbon::now()->toDateTimeString() . '| Reading uploaded file.....');
        if (!empty($path) && file_exists($path)) {
            //load record from file to redis
            try {
                Excel::load($path, function ($reader) use ($job) {
                    $objExcel = $reader->getExcel();
                    $sheet = $objExcel->getSheet(0);
                    $highestRow = $sheet->getHighestRow();
                    $highestColumn = $sheet->getHighestColumn();

                    //computing
                    $nummsisdn = 0;
                    $job_id = $job->id;
                    $row_number = (int)(empty($job->row_number)?0:$job->row_number);
                    $flash = $job->flash;
                    $sender = $job->sender;
                    $message = preg_replace(array('/\r\n/'), array("\n"), trim($job->message));

                    //  Loop through each row of the worksheet in turn
                    $rowData = $sheet->rangeToArray('A' . 1 . ':' . $highestColumn . 1, NULL, TRUE, FALSE);
                    $headers = $rowData[0];
                    //save msg sent to msisdn, to prevent dupplicate
                    $processed_msisdn = [];
                    $total_amount = 0.0;
                    $this->info(Carbon::now()->toDateTimeString() . '| Sending Bulk SMS Now...');

                    $q1 = "SELECT new_bal, username FROM credits WHERE user_id = '{$job->user_id}'";
                    $result11 = DB::select($q1);
                    $curr_bal = $result11[0]->new_bal;

                    for ($row = 1; $row <= $highestRow; $row++) {
                        if($row <= $row_number) continue;
                        try {
                            //check stop service flag
                            if (!ServiceTools::can_run_service()) {
                                //update job status
                                $job->progress = (double)(100 * $row) / $highestRow;
                                $job->row_number = $row;
                                $job->status = 'stopped';
                                $job->save();
                                $this->info('Service Stopped By User!');
                                exit(1);
                            }
                            //commit every 10% of progress
                            $progress = (double)(100 * $row) / $highestRow;
                            if ($row > 1 && $progress % 10 === 0) {
                                $job->progress = $progress;
                                $job->row_number = $row;
                                $job->save();
                            }
                            //  Read a row of data into an array
                            $rowData = $sheet->rangeToArray('A' . $row . ':' . $highestColumn . $row, NULL, TRUE, FALSE);
                            $number = $rowData[0][0];
                            $msisdn = SmsTools::msisdn_prep(preg_replace('/\D/', '', $number));
                            $msisdn = trim($msisdn);
                            $msisdn = SmsTools::format_phone_number($msisdn);

                            if (is_numeric($msisdn) && !empty($msisdn)) {
                                //check for duplicate
                                if ($job->remove_duplicate && in_array($msisdn, $processed_msisdn)) {
                                    continue;
                                }
                                $updated_msg = self::update_msg($headers, $rowData[0], $message);

                                //prepare sms to be sent
                                $sms = new Sms();
                                $sms->job_id = $job_id;
                                $sms->user_id = $job->user_id;
                                $sms->sender = $sender;
                                $sms->msisdn = $msisdn;
                                $sms->message = $updated_msg;
                                $sms->flash = $flash;
                                $sms->log = $this->log_path;

                                $cost = $this->get_cost($msisdn, $message, $job->user_id);
                                $total_amount += $cost;

                                if($curr_bal < $total_amount) {
                                    $job->status = 'low_balance';
                                    $job->progress = $progress;
                                    $job->row_number = $row -1;
                                    $job->save();
                                    $this->info('Client has low balance!');
                                    SendLowBalanceAlertToClient::dispatch($job->user, $job)->onConnection('redis')->onQueue('notification');
                                    SendLowBalanceAlertToSupport::dispatch($job->user, $job)->onConnection('redis')->onQueue('notification');
                                    exit(0);
                                }
                                SendSms::dispatch($sms)->onConnection('redis')->onQueue('bulksms');
                                $nummsisdn++;
                                array_push($processed_msisdn, $msisdn);
                            } else {
                                continue;
                            }
                        } catch (\PDOException $pdoex){
                            $this->info('Database Error: '.$pdoex->getMessage().'. The Cron Process has been terminated!');
                            exit(3);
                        } catch (\Throwable $e) {
                            $this->error('Application Error: '.$e->getMessage());
                        }
                        $row_number++;
                        //sleep for 25 milliseconds to prevent Lock wait timeout exceeded error on database
                        usleep(25000);
                    }

                    $job->progress = 100;
                    $job->row_number = $highestRow;
                    $job->save();

                    $this->info($nummsisdn . ' sms sent for this campaign');
                });
            } catch (\Exception $e) {
                $this->error($e->getMessage());
                ErrorReporting::report($e);
            }
        } else {
            $this->error('File Not Found');
        }

        //mark job as complete
        $job->status = 'done';
        $job->task_completed_date = Carbon::now()->toDateTimeString();
        $job->save();
        $this->info('Task Complete!');
    }

    private function get_cost($numb, $msgs, $user_id) {
        $no_chars = strlen($msgs);
        $no_pages = $this->no_pages($no_chars);
        $cost = 0.0;
        $msisdn = preg_replace('/\D/', '', $numb);

        if (is_numeric($msisdn)) {
            if (substr($msisdn, 0, 1) == '1') {
                $code = substr($msisdn, 0, 1);
                $ccLength = strlen($code);
            } elseif (substr($msisdn, 0, 1) == '9') {
                $code = substr($msisdn, 0, 1);
                $ccLength = strlen($code);
            } elseif (substr($msisdn, 0, 2) == '44') {
                $code = substr($msisdn, 0, 2);
                $ccLength = strlen($code);
            } elseif (substr($msisdn, 0, 2) == '27') {
                $code = substr($msisdn, 0, 2);
                $ccLength = strlen($code);
            } else {
                $code = substr($msisdn, 0, 3);
                $ccLength = strlen($code);
            }
            $standard_len = $ccLength + $this->checkCode($code);
            $new_len = strlen($msisdn);
            if ($standard_len === $new_len) {
                $userNetwork = $this->get_user_sms_network($user_id, $msisdn);
                $cost += ($no_pages * $userNetwork['sms_price']);
            }
        }

        return $cost;
    }

    private function get_user_sms_network($userId, $msisdn) {
        $sms_network = [];
        $query = "SELECT * FROM networks";
        $res = DB::select($query);
        if (!empty($res) && count($res)>0) {
            foreach ($res as $network) {
                $prefixes = explode(',', $network->prefix);
                foreach ($prefixes as $prefix) {
                    $prefix = trim($prefix);
                    if (strpos($msisdn, $prefix) === 0) {
                        $network_id = $network->id;
                        $country_id = $network->country_id;
                        $query = "SELECT * from user_networks where user_id='$userId' and network_id='$network_id' and country_id='$country_id' LIMIT 0,1";
                        $res2 = DB::select($query);
                        if (!empty($res2) && count($res2)>0) {
                            $usr_price = $res2[0];
                            $sms_network = ['network_id' => $network_id, 'network' => $network->nice_name, 'sms_price' => $usr_price->sms_price];
                        } else {
                            $sms_network = ['network_id' => $network_id, 'network' => $network->nice_name, 'sms_price' => $network->sms_price];
                        }
                    }
                }

            }
        }
        if (!empty($sms_network))
            return $sms_network;
        $query = "select price from credits where user_id='$userId' limit 0,1";
        $res = DB::select($query);
        if (!empty($res) && count($res)>0) {
            $record = $res[0];
            return ['network_id' => null, 'network' => null, 'sms_price' => $record->price];
        }
        return ['network_id' => null, 'network' => null, 'sms_price' => null];
    }

    private function no_pages($no_chars) {

        if ($no_chars <= 160) {
            $no_pages = 1;
        }
        if ($no_chars > 160 && $no_chars <= 306) {
            $no_pages = 2;
        }
        if ($no_chars > 306 && $no_chars <= 459) {
            $no_pages = 3;
        }
        if ($no_chars > 459 && $no_chars <= 621) {
            $no_pages = 4;
        }
        if ($no_chars > 621) {
            $no_pages = 5;
        }

        return $no_pages;
    }

    private function checkCode($code) {
        if ($code == '246') {
            $msisdnLeng = 6;
            return $msisdnLeng;
        } elseif ($code == '238' || $code == '241' || $code == '239' || $code == '220') {
            $msisdnLeng = 7;
            return $msisdnLeng;
        } elseif ($code == '64' || $code == '229' || $code == '257' || $code == '237' || $code == '236' || $code == '235' || $code == '225' || $code == '243' || $code == '231' || $code == '223' || $code == '227' || $code == '228') {
            $msisdnLeng = 8;
            return $msisdnLeng;
        } elseif ($code == '242' || $code == '240' || $code == '254' || $code == '265' || $code == '250' || $code == '27' || $code == '221' || $code == '232' || $code == '211' || $code == '255' || $code == '256' || $code == '260' || $code == '233' || $code == '972') {
            $msisdnLeng = 9;
            return $msisdnLeng;
        } elseif ($code == '1' || $code == '234' || $code == '218' || $code == '263' || $code == '44' || $code == '96') {
            return $msisdnLeng = 10;
        } elseif ($code == '9') {
            return $msisdnLeng = 11;
        } else {
            $msisdnLeng = 10;
            return $msisdnLeng;
        }
    }

}
