<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Models\Trigger;
use App\Models\TriggerSchedule;
use App\Models\TriggerScheduleStat;
use App\Models\TriggerScheduleStatLog;
use App\Models\SendingServer;
use App\Models\Contact;
use App\Models\Lists;
use App\Http\Helper\Helper;
use App\Services\ElasticEmailService;
use App\Services\SendGridService;
use App\Services\RssFeedService;
use DB;

class RunTriggersCampaigns extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'run:triggers-campaigns';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Send campaigns that have been scheduled for tirggers';

    /**
     * Execute the console command.
     */
    public function handle()
    {
        // Get active triggres and not in progress to matain speed if defined
        $triggers = Trigger::whereInProgress(false) // should be false, true due to teting
          ->whereIsActive(true)
          ->whereAction('send_campaign')
          ->get();

        if(!empty($triggers)) {
          // Get Settings
          $settings = DB::table('settings')->whereId(config('custom.app_id'))->first();
          $tracking = $settings->tracking == 'enabled' ? true : false;
          $app_url = $settings->app_url;

          foreach($triggers as $trigger) {

            // Next cron will not enterin this trigger sending until finish
            Trigger::whereId($trigger->id)->update(['in_progress' => true]);

            // Save data for stats if not exist
            if(!TriggerScheduleStat::whereTriggerId($trigger->id)->exists()) {
              $trigger_schedule_stat = TriggerScheduleStat::create([
                'trigger_id' => $trigger->id,
                'trigger_name' => $trigger->name,
                'schedule_by' => \App\Models\User::getUserValue($trigger->user_id, 'name'),
                'email_subject' => json_decode($trigger->attributes)->email_subject ?? '',
                'content' => json_decode($trigger->attributes)->content_html ?? '',
                'app_id' => $trigger->app_id,
                'user_id' => $trigger->user_id,
              ]);
              $trigger_schedule_stat_id = $trigger_schedule_stat->id;
            } else {
              $trigger_schedule_stat_id = TriggerScheduleStat::whereTriggerId($trigger->id)->value('id');
            }

            $sending_speed = json_decode($trigger->attributes)->sending_speed;
            if(!empty($sending_speed) && $sending_speed == 'limited') {
              $sending_limit = json_decode($trigger->attributes)->sending_limit;
              $wait = floor(3600 / $sending_limit); // divide on 1 hour
            } else {
              $wait = false;
            }

            // mail headers will be set for admin 
            $mail_headers = json_decode(DB::table('users')->select('mail_headers')->whereAppId($trigger->app_id)->whereParentId(0)->value('mail_headers'), true);


            $query = TriggerSchedule::whereTriggerId($trigger->id)->where('send_datetime', '<=', \Carbon\Carbon::now());
            $query->chunk(1000, function($trigger_schedule) use ($settings, $tracking, $app_url, $trigger_schedule_stat_id, $trigger, $wait, $mail_headers) {

              foreach($trigger_schedule as $schedule) {

                // stop sending if trigger is inactive
                if(!Trigger::whereId($trigger->id)->value('is_active')) {
                    Trigger::whereId($trigger->id)->update(['in_progress' => false]);
                    exit;
                }
                // If bounced
                $contact = Contact::whereId($schedule->contact_id)->with('list')->first();

                if(!filter_var($contact->email, FILTER_VALIDATE_EMAIL)) {
                  TriggerSchedule::whereId($schedule->id)->delete();
                  continue;
                }

                // If already sent to a contact
                if(TriggerScheduleStatLog::whereTriggerScheduleStatId($trigger_schedule_stat_id)->whereEmail($contact->email)->exists()){
                  // Delete data from trigger_schedules table after processed
                  TriggerSchedule::whereId($schedule->id)->delete();
                  continue;
                }

                if(DB::table('global_bounces')->whereEmail($contact->email)->exists()) {
                  // Delete data from trigger_schedules table after processed
                  TriggerSchedule::whereId($schedule->id)->delete();
                  continue;
                }

                // Get Sending Sever
                try {
                  $sending_server = SendingServer::whereStatus('Active')->whereId($schedule->sending_server_id)->with('bounce')->first()->toArray();
                } catch(\Exception $e) {
                  continue;
                }

                // If sending server is inactive
                if(empty($sending_server)) continue;

                $connection = Helper::configureSendingNode($sending_server['type'], $sending_server['sending_attributes']);

                if($connection['success']) {
                  $sending_domain = Helper::getSendingDomainFromEmail($sending_server['from_email']);
                  // if no domain found
                  try {
                    $domain = $sending_domain->protocol.$sending_domain->domain;
                  } catch(\Exception $e) {
                    continue;
                  }
                  $message_id = Helper::getCustomMessageID($domain);

                  if(!empty($sending_server['tracking_domain'])) {
                    $tracking_domain = $sending_server['tracking_domain'];
                  } else {
                    $tracking_domain = Helper::getAppURL();
                  }

                // render RSS Feed
                $content = (new RssFeedService())->parseAndRender($schedule->content_html);

                // Replace spintags
                $content = Helper::replaceSpintags(Helper::decodeString($content));
                $email_subject = Helper::replaceSpintags(Helper::decodeString($schedule->email_subject));

                // Replace custom field
                $content = Helper::replaceCustomFields($content, $contact->customFields);
                $email_subject = Helper::replaceCustomFields($email_subject, $contact->customFields);

                // Replace Spintax [Random: hi|hello|hey]
                $content = Helper::spinTaxParser($content);
                $email_subject = Helper::spinTaxParser($email_subject);

                // Default Parser
                $content = Helper::defaultValueParser($content);
                $email_subject = Helper::defaultValueParser($email_subject);

                if($schedule->from_detail == 'custom') {
                  $from_detail_custom = json_decode($schedule->from_detail_custom);
                  $from_name = $from_detail_custom->from_name;
                  $from_email = $from_detail_custom->from_email;
                  $reply_email = $from_detail_custom->reply_email;
                } elseif($schedule->from_detail == 'list') {
                  $from_name = $contact->list->from_name;
                  $from_email = $contact->list->from_email;
                  $reply_email = $contact->list->reply_email;
                } else {
                  $from_name = $sending_server['from_name'];
                  $from_email = $sending_server['from_email'];
                  $reply_email = $sending_server['reply_email'];
                }

                $from_name = Helper::replaceSpintags($from_name);
                $reply_email = filter_var($reply_email, FILTER_VALIDATE_EMAIL) ? $reply_email : null;

                  

                  // Create AutoFollowupStatLog and the id would be use to track the email
                  $list_name = $contact->list->name;
                  $trigger_schedule_stat_log_data = [
                    'trigger_schedule_stat_id' => $trigger_schedule_stat_id,
                    'message_id' => $message_id,
                    'email' => $contact->email,
                    'list' => $list_name,
                    'sending_server' => $sending_server['name'],
                  ];

                  $trigger_schedule_stat_log = TriggerScheduleStatLog::create($trigger_schedule_stat_log_data);

                  // Data that will be use to replce the system variables
                  $data_values = [
                    'sender-name'    => $from_name,
                    'sender-email'   => $sending_server['from_email'],
                    'domain'         => $tracking_domain,
                    'message-id'     => $message_id,
                    'campaign-id'    => base64_encode($trigger_schedule_stat_log->id),
                    'type'           => 'st'
                  ];

                  // Replace system variables
                  $email_subject = Helper::replaceSystemVariables($contact, $email_subject, $data_values);
                  $content = Helper::replaceSystemVariables($contact, $content, $data_values);

                  // for to get replies info
                  $content .= "<span id='rz-ref-trigger-{$trigger_schedule_stat_log->id}-{$trigger->app_id}-rz-ref'></span>";

                  if($tracking) {
                    // click tracking should be before track_opens becuase don't want to convert that url
                    $content = Helper::convertLinksForClickTracking($trigger_schedule_stat_log->id, $tracking_domain, $content, $app_url, '/ts/click/');

                    // Make open tracking url and pixel ts=track_schedule
                    $track_open = $tracking_domain.'/ts/open/'.base64_encode($trigger_schedule_stat_log->id);
                    $content .= "<div style='float:left; clear:both; font-family:Arial; margin:40px auto; width:100%; line-height:175%; font-size:11px; color:#434343'><img border='0' src='".$track_open."' width='1' height='1' alt=''></div>";
                  }

                  // If sending type that supported by framework will be send with a same way
                  if(in_array($sending_server['type'], Helper::sendingServersFramworkSuported())) {

                    $message = new \Symfony\Component\Mime\Email();
                    $message->from(new \Symfony\Component\Mime\Address($from_email, "$from_name"));
                    $message->to($contact->email);
                    $message->subject($email_subject);
                    !empty($reply_email) ? $message->replyTo($reply_email) : '';
                    if(!empty($sending_server['bounce']['email'])) {
                      $message->returnPath($sending_server['bounce']['email']);
                    }

                    // adding the envelope becuase bounce email having issue
                    $envelope = new \Symfony\Component\Mailer\Envelope(
                        new \Symfony\Component\Mime\Address($from_email, "$from_name"), // From email
                        [new \Symfony\Component\Mime\Address($contact->email)] // Envelope recipient(s)
                    );

                    $headers= $message->getHeaders();
                    $headers->addIdHeader('Message-ID', $message_id);
                    // Header will use to process the bounces and fbls etc.
                    $headers->addTextHeader('RZ-Type-ID', "trigger-{$trigger_schedule_stat_log->id}-{$schedule->app_id}");
                    // Required header for good inboxing
                    $headers->addTextHeader('List-Unsubscribe', sprintf('<mailto:%s?subject=unsubscribe>', urlencode($from_email)));
                    $headers->addTextHeader('List-Unsubscribe-Post', 'List-Unsubscribe=One-Click');

                    // Header will be use to process reports for Amazon
                    if($sending_server['type'] == 'amazon_ses_api' && !empty(json_decode($sending_server['sending_attributes'])->process_reports) && json_decode($sending_server['sending_attributes'])->process_reports == 'yes') {
                      if(!empty(json_decode($sending_server['sending_attributes'])->amazon_configuration_set)) {
                        $headers->addTextHeader('X-SES-CONFIGURATION-SET', json_decode($sending_server['sending_attributes'])->amazon_configuration_set);
                      }
                    }

                    if(!empty($mail_headers)) {
                      foreach($mail_headers as $header_key => $header_value) {
                        $header_value = Helper::replaceSpintags($header_value);
                        $header_value = Helper::replaceCustomFields($header_value, $contact->customFields);
                        $header_value = Helper::replaceSystemVariables($contact, $header_value, $data_values);
                        $header_value = Helper::spinTaxParser($header_value);

                        $header_key = Helper::replaceHyphen($header_key);

                        $message->getHeaders()->addTextHeader($header_key, $header_value);
                      }
                    }

                    $message->html($content);
                    $message->text(htmlspecialchars_decode(strip_tags($content)));

                      try {
                        $connection['transport']->send($message);
                        $status = 'Sent';
                      } catch(\Exception $e) {
                        \Log::error('run:triggers-campaigns => '.$e->getMessage());
                        $status = 'Failed';
                      }
                  } elseif($sending_server['type'] == 'sendgrid_api') {
                    try {
                        SendGridService::send(json_decode($sending_server['sending_attributes'])->api_key,
                        $from_email, $from_name, $contact->email, $reply_email, $email_subject, $content, 'html', $message_id,
                        );
                        $status = 'Sent';
                      } catch(\Exception $e) {
                        \Log::error('run:triggers-sendgrid => '.$e->getMessage());
                        $status = 'Failed';
                      }
                  } elseif($sending_server['type'] == 'elastic_email_api') {
                      try {
                        $load_sending_servers = false;
                        ElasticEmailService::send(json_decode($sending_server['sending_attributes'])->api_key,
                          $from_email, $from_name, $contact->email, $reply_email, $email_subject, $content, $message_id
                        );
                        $status = 'Sent';
                      } catch(\Exception $e) {
                        \Log::error('run:triggers-elastic_email => '.$e->getMessage());
                        $status = 'Failed';
                      }
                  } elseif($sending_server['type'] == 'postal_api') {
                    try {
                      $result = \App\Services\PostalAPIService::send(
                          json_decode($sending_server['sending_attributes'])->postal_server_url,
                          json_decode($sending_server['sending_attributes'])->api_key,
                          $from_email, $from_name, $contact->email, $reply_email, $email_subject, $content, 'html', $message_id
                      );
                      if($result === 'send') {
                          $status = 'Sent';
                      } else {
                          $status = 'Failed';
                      }
                    } catch(\Exception $e) {
                      \Log::error('run:triggers-postal => '.$e->getMessage());
                      $status = 'Failed';
                    }
                  }

                  // Should be check in both case
                  //if($status == 'Sent') {
                    $sending_server_data = Helper::updateSendingServerCounters($sending_server['id']);
                  //}

                  // Update status
                  TriggerScheduleStatLog::whereId($trigger_schedule_stat_log->id)->update(['status' => $status]);

                  // Delete data from trigger_schedules table after processed
                  TriggerSchedule::whereId($schedule->id)->delete();

                  if($wait) sleep($wait);

                } else {
                  // If sending server connection failed then need to update it as system inactive
                  // Removing single and double quote due to output issue with js alert at frontend 
                  SendingServer::whereId($sending_server['id'])->update(['status' => 'System Inactive', 'notification' => str_replace( ["'",'"'], '', explode('.',$connection['msg'])[0] )]);
                }
              } // End foreach contacts
            });

            // Next cron should enterin this trigger sending now
            Trigger::whereId($trigger->id)->update(['in_progress' => false]);
          } // End foreach triggers
        } // If triggers are not empty
    }
}
