<?php

namespace App\Jobs;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use App\Models\ScheduleCampaign;
use App\Http\Helper\Helper;
use League\Csv\Writer;
use DB;


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

    private $processed = 0;
    private $inactive =  0;
    private $unsubscribed = 0;
    private $suppressed = 0;
    private $bounced = 0;
    private $spammed = 0;
    private $duplicates = 0;
    private $batch_no = 0;
    private $i = 0;
    private $batch_size = 5000;
    private $old_email = null;
    private $writer = null;
    protected $schedule_campaign_id;
    public $tries = 3;

    /**
     * Create a new job instance.
     */
    public function __construct($id)
    {
        $this->schedule_campaign_id = $id;
    }

    /**
     * Execute the job.
     */
    public function handle(): void
    {
        
    $schedule = ScheduleCampaign::whereId($this->schedule_campaign_id)->whereStatus('Preparing')->first();
    if(!$schedule) exit;

    if(json_decode($schedule->sending_speed)->speed == 'limited') {
      $email_per_batch = json_decode($schedule->sending_speed)->limit;
    } else {
      $email_per_batch = $this->batch_size;
    }

    $path_schedule_campaign = str_replace('[user-id]', $schedule->user_id, config('custom.path_schedule_campaign'));
    $path_schedule_campaign .= $this->schedule_campaign_id;

    $list_ids = explode(',', $schedule->list_ids);

    // without distinct
    $total = DB::table('contacts')->whereIn('list_id', $list_ids)->count();

    ScheduleCampaign::whereId($this->schedule_campaign_id)->update([
      'total' => $total
    ]);



    // if alread exist, it normally happanes for large campaing
    if(!\App\Models\ScheduleCampaignStat::whereScheduleCampaignId($schedule->id)->exists()) {
      // Create record for schedule_campaing_stats table also
      $schedule_by = \App\Models\User::getUserValue($schedule->user_id, 'name');
      $opens_clicks = json_encode([
          "total_opens" => 0,
          "unique_opens" => 0,
          "total_clicks" => 0,
          "unique_clicks"=> 0
      ]);
      $stat_data = [
        'schedule_campaign_id'   => $schedule->id,
        'schedule_campaign_name' => $schedule->name,
        'email_subject'          => $schedule->email_subject,
        'content'          => $schedule->content,
        'schedule_by'      => $schedule_by,
        'threads'          => $schedule->threads,
        'total'            => $total,
        'scheduled'        => 0,
        'sent'             => 0,
        'opens_clicks'      => $opens_clicks,
        'start_datetime'   => $schedule->send_datetime,
        'sending_speed'    => $schedule->sending_speed,
        'app_id'           => $schedule->app_id,
        'user_id'          => $schedule->user_id,
        'created_at'       => $schedule->created_at
      ];
      $schedule_stat = \App\Models\ScheduleCampaignStat::create($stat_data);

      \App\Models\Contact::whereIn('list_id', $list_ids)
        ->with('list')
        ->with('customFields')
        ->orderBy('email')
        ->orderBy('id')
        ->chunk($this->batch_size, function ($contacts) use ($email_per_batch, $path_schedule_campaign, $schedule) {

          foreach ($contacts as $contact) {
            // Check inactive
            if(!$contact->is_active) { $this->inactive++; continue; }

            // Check unsubscribed
            if($contact->is_unsubscribed) { $this->unsubscribed++; continue; }

            // Check suppressed
            if(Helper::isSuppressed($contact->email, $schedule->app_id)) { $this->suppressed++; continue; }

            // Skip duplicates; group by clause mantain same emails consecutives
            if($contact->email == $this->old_email) { $this->duplicates++; continue; }

            $this->old_email = $contact->email; // Keep old email record to compare next time 

            // Check bounced
            if($contact->is_bounced) { $this->bounced++; continue; }


            // Check spammed
            $is_spammed = DB::table('global_spams')
              ->whereEmail($contact->email)
              ->exists();
            if($is_spammed)  { $this->spammed++; continue; }

            if($this->batch_no == 0 || $this->i >= $email_per_batch) {
              // Need to start file with 1
              $this->batch_no++;
              Helper::dirCreateOrExist($path_schedule_campaign); //Create dir if not exist
              $file = $path_schedule_campaign.DIRECTORY_SEPARATOR.$this->batch_no.'.csv';
              // 5 sec break;
              sleep(5);

              // Sometime it restart to write the file so handel it
              if(file_exists($file)) exit;

              $this->writer = Writer::createFromPath($file, 'w+'); // Create a .csv file to write data
              //chmod($file, 0777);  // File Permission
              $data = [
                'CONTACT_ID', 
                'EMAIL',
                'LIST',
                'EMAIL_SUBJECT',
                'FROM_NAME',
                'FROM_EMAIL',
                'REPLY_EMAIL',
                'BROADCAST',
              ];
              $this->writer->insertOne($data); // Write data into file
              $this->i = 0;

              // Need to start campaign when 1 file ready to send
              if($this->batch_no > 1) {
                ScheduleCampaign::whereId($this->schedule_campaign_id)->update([
                  'total_threads' => $this->batch_no
                ]);

                // Only need to make schedule for when 1st complete
                if($this->batch_no == 2) {
                  ScheduleCampaign::whereId($this->schedule_campaign_id)->update([
                    'status' => 'Scheduled'
                  ]);
                }
              }
              
            }

            $broadcast_content = $schedule->content;

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

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

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

            // Default Parser
            $broadcast_content = Helper::defaultValueParser($broadcast_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 = $from_email = $reply_email = '';
            }

            $data = [
              $contact->id,
              $contact->email,
              Helper::decodeString($contact->list->name),
              $email_subject,
              Helper::decodeString($from_name),
              $from_email,
              $reply_email,
              $broadcast_content
            ];
            $this->writer->insertOne($data); // Write data into file
            $this->i++;
            $this->processed++;

            // Increment in scheduled
            $schedule->increment('scheduled');
          }
        });

        $scheduled_detail = [
          'Total'        => $total,
          'Scheduled'    => $this->processed,
          'Inactive'     => $this->inactive,
          'Unsubscribed' => $this->unsubscribed,
          'Duplicates'   => $this->duplicates,
          'Suppressed'   => $this->suppressed,
          'Bounced'      => $this->bounced,
          'Spammed'      => $this->spammed,
        ];

        // If there is only 1 batch then upper condition will not execute or no one passed for schedule
        if($this->batch_no == 1 || $this->batch_no == 0) {
          ScheduleCampaign::whereId($this->schedule_campaign_id)->update([
            'status' => 'Scheduled'
          ]);
        }

        // Update schedule_campaigns table 
        ScheduleCampaign::whereId($this->schedule_campaign_id)->update([
          'scheduled' => $this->processed,
          'total_threads'   => $this->batch_no,
          'scheduled_detail' => json_encode($scheduled_detail),
        ]);

        
        // Update schedule_campaigns table 
        \App\Models\ScheduleCampaignStat::whereId($schedule_stat->id)->update([
          'scheduled_detail' => json_encode($scheduled_detail),
          'scheduled'        => $this->processed,
        ]);

        // Set notificatoin
        $notification_name = $schedule->name . ' ' .__('app.scheduled_successfully');
        $attributes = [
          'file' => null
        ];
        $notification_attributes = json_encode($attributes);
        $notification = [
          'name' => $notification_name,
          'type' => 'other',
          'attributes' => $notification_attributes,
          'app_id' => $schedule->app_id,
          'user_id' => $schedule->user_id
        ];
        \App\Models\Notification::create($notification);
      }

    }
}
