<?php

namespace App\Console\Commands;

use App\CronJobStat;
use App\PendingSingleEmail;
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 Bogardo\Mailgun\Facades\Mailgun;
use Carbon\Carbon;
use Illuminate\Console\Command;
use Illuminate\Support\Facades\DB;
use Maatwebsite\Excel\Facades\Excel;

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

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Send Single Email!';

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

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        //update & check cron job status for sending bulk sms
        $this->update_cron_job_status_running('SINGLE_EMAIL', 'RUNNING');

        //init vars
        $cdate = Carbon::now();
        $cron_status_running = true;

        while (Carbon::now()->diffInSeconds($cdate) <= 53) {
            try {
                DB::table('pending_single_emails')
                    ->where('sched_datetime', '<=', Carbon::now()->toDateTimeString())
                    ->select('id')
                    ->orderBy('id')
                    ->chunk(1000, function ($jo) use (&$cron_status_running) {
                        $this->check_for_stop_flag();
                        $this->info('Fetched ' . count($jo) . ' pending email');

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

                            foreach ($jo as $j) {
                                try {
                                    //check stop service flag
                                    if (!ServiceTools::can_run_service()) {
                                        $this->info('Service Stopped By User!');
                                        DB::commit();
                                        $this->update_cron_job_status_done('SINGLE_EMAIL', 'DONE');
                                        exit(1);
                                    }
                                    $pendingEmail = PendingSingleEmail::find($j->id);
                                    //computing
                                    $user = User::findOrFail($pendingEmail->user_id);
                                    $credit = $user->credit;

                                    //prepare statement for sms_log
                                    $job_id = $user->username . '_' . date('YmdHis');
                                    $user_id = $pendingEmail->user_id;
                                    $username = $user->username;
                                    $sender = $pendingEmail->sender;
                                    $message = $pendingEmail->message;
                                    $subject = $pendingEmail->subject;
                                    $submit_date = $pendingEmail->sched_datetime;
                                    $status = 'PENDING';
                                    $originated = $pendingEmail->type;
                                    $created_at = Carbon::now()->toDateTimeString();
                                    $email_price = null;

                                    //  Loop through each row of the worksheet in turn
                                    //save msg sent to msisdn, to prevent dupplicate

                                    //  Read a row of data into an array
                                    $email = $pendingEmail->email;

                                    $id = uniqid('NE', true);

                                    if (filter_var($email, FILTER_VALIDATE_EMAIL)) {
                                        //prepare sms to be sent
                                        $email_price = $credit->email_price;

                                        //Send Email
                                        $data = Mailgun::raw($message, function ($msg) use ($subject, $email, $sender) {
                                            $msg->to($email)->subject($subject)->from($sender);
                                            $msg->trackOpens(true);
                                        });
//                                    $this->info(var_export($data, true));

                                        $msgId = str_replace(['<', '>'], ['', ''], $data->id);
                                        $resp_code = (int)$data->status;
                                        $response = $data->message;
                                        $status = !empty($msgId) ? 'SENT' : 'FAILED';

                                        //update balance
                                        $new_bal = (double)$user->credit->new_bal;
                                        $new_credit_used = (double)$user->credit->credit_used;

                                        $new_bal -= (double)$email_price;
                                        $new_credit_used += (double)$email_price;
//                                        $new_bal = $user->credit->new_bal - $email_price;
//                                        $new_credit_used = $user->credit->credit_used + $email_price;
                                        $credo = $user->credit;
                                        $credo->new_bal = (string) $new_bal;
                                        $credo->credit_used = (string) $new_credit_used;
                                        $credo->save();

                                        DB::insert('INSERT INTO `email_logs`
                                (id, job_id, user_id, username, email, subject, sender, message, response, resp_code,
                                submit_date, status, created_by, originated, created_at, email_price, msgid)
                                  VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)', [$id, $job_id, $user_id, $username,
                                            $email, $subject, $sender, $message, $response, $resp_code, $submit_date,
                                            $status, $username, $originated, $created_at, $email_price, $msgId]);

                                    } else {
                                        $status = 'FAILED';
                                        DB::insert('INSERT INTO `email_logs`
                                (id, job_id, user_id, username, email, subject, sender, message, 
                                submit_date, status, created_by, originated, created_at, email_price)
                                  VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?)', [$id, $job_id, $user_id, $username,
                                            $email, $subject, $sender, $message, $submit_date,
                                            $status, $username, $originated, $created_at, $email_price]);
                                    }

                                } catch (\PDOException $pdoex) {
                                    $this->info('Database Error: ' . $pdoex->getMessage());
                                    $this->update_cron_job_status_done('SINGLE_EMAIL', 'DONE');
                                    exit(3);
                                } catch (\ErrorException $eex) {
                                    //commit transaction
                                    DB::commit();
                                    $this->update_cron_job_status_done('SINGLE_EMAIL', 'DONE');
                                    $this->info('Error: ' . $eex->getMessage());
                                    exit(3);
                                } catch (\RuntimeException $re) {
                                    EmailAlert::insertError('SendSingleEmail', 'Msisdn: ' . $pendingEmail->msisdn . ' - ' . $re->getCode() . ' - ' . $re->getMessage(), $re->getLine());
                                } catch (\Exception $e) {
                                    $this->error($e->getMessage());
                                    EmailAlert::insertError('SendSingleEmail', 'Msisdn: ' . $pendingEmail->msisdn . ' - ' . $e->getCode() . ' - ' . $e->getMessage(), $e->getLine());
                                }
                                $pendingEmail->delete();
                                $this->info('Task Complete!');
                            }

                            // Close connection
                            DB::commit();
                        } catch (\RuntimeException $re) {
                            EmailAlert::insertError('SendSingleEmail', $re->getMessage(), $re->getLine());
                        } catch (\Exception $e) {
                            EmailAlert::insertError('SendSingleEmail', $e->getMessage(), $e->getLine());
                        } catch (\Throwable $t) {
                            if ($cron_status_running === true) {
                                //update status of the running cron job
                                $this->update_cron_job_status_done('SINGLE_EMAIL', 'DONE');
                                $cron_status_running = false;
                            }
                            throw $t;
                        }
                    });
            } catch (\Throwable $t) {
                if ($cron_status_running === true) {
                    //update status of the running cron job
                    $this->update_cron_job_status_done('SINGLE_EMAIL', 'DONE');
                }
                throw $t;
            }
            sleep(1);
        }

        //update status of the running cron job
        if ($cron_status_running === true)
            $this->update_cron_job_status_done('SINGLE_EMAIL', '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 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_EMAIL', 'DONE');
            exit(1);
        }
    }

}
