<?php

namespace App\Jobs;

use ArrayIterator;
use App\Models\User;
use MultipleIterator;
use App\Models\Resource;
use App\Models\SMTPMails;
use App\Mail\SendReportMail;
use Illuminate\Http\Request;
use App\Mails\ReportTestMail;
use App\Models\ScheduledTask;
use Illuminate\Bus\Queueable;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Queue\SerializesModels;
use App\Services\ScheduledReportService;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use App\Http\Middleware\DeleteAttachmentAfterJob;
use App\Mails\ScheduledTaskMail\ScheduledReportMail;

class SendScheduledSingleReportJob implements ShouldQueue
{
    use Dispatchable;
    use InteractsWithQueue;
    use Queueable;
    use SerializesModels;

    public $task;
    public $report;
    public $receivers;
    public $body;

    public $beginTime;
    public $totalSent;
    public $totalFailed;
    public $sendIds;
    public $manual;

    public function __construct(ScheduledTask $task, $report, $receivers, $body, $manual = false)
    {
        $this->task = $task;
        $this->report = $report;
        $this->receivers = $receivers;
        $this->body = $body;

        $this->beginTime = now();
        $this->totalSent = 0;
        $this->totalFailed = 0;
        $this->sendIds = [];
        $this->manual = $manual;
    }


    /**
     * Handles the process of sending scheduled reports and updating task status.
     *
     * This method performs the following tasks:
     * 1. Retrieves the email information for the receivers and body.
     * 2. Checks if SMTP configuration exists.
     * 3. Attempts to send emails to the receivers. If sending fails for any receiver, updates the task as incomplete.
     * 4. If all emails are sent successfully, updates the task as completed successfully. Otherwise, updates the task as failed or incomplete.
     *
     * @tag srm_schdular
     */
    public function handle(): void
    {

        $mailInfo = $this->getEmailInfoIterator($this->receivers, $this->body);
        $this->updateIfManual($this->task, ['recieved_users' => null,'retry_count' => 0]);

        try {
            $this->checkSMTPExist();
        } catch (\Exception $exception) {

            $this->totalFailed = count($this->receivers);

            updateFailedTask(
                $this->task,
                $this->manual,
                $exception,
                $this->beginTime,
                $this->totalSent,
                $this->totalFailed,
                $this->sendIds,
                'failed'
            );

            return; // Exit the handle method early since SMTP check failed
        }

        foreach ($mailInfo as [$receiver, $body]) {
            try {
                (new ScheduledReportMail($this->task, $this->report, $receiver, $body))->send();
                $this->totalSent++;
                $this->sendIds[] = $receiver['id'];
            } catch (\Exception $error) {
                $this->totalFailed++;
                $lastError = $error; // Store the last occurred exception

            }
        }
        if ($this->totalSent > 0 && $this->totalFailed > 0) {
            updateFailedTask(
                $this->task,
                $this->manual,
                !is_null($lastError) ? $lastError : new \Exception('There is an error'),
                $this->beginTime,
                $this->totalSent,
                $this->totalFailed,
                $this->sendIds,
                'incomplete'
            );
        } elseif ($this->totalSent == 0) {
            updateFailedTask(
                $this->task,
                $this->manual,
                !is_null($lastError) ? $lastError : new \Exception('There is an error'),
                $this->beginTime,
                $this->totalSent,
                $this->totalFailed,
                $this->sendIds,
                'failed'
            );
        }
        if($this->totalFailed == 0 && $this->totalSent == count($this->receivers)) {
            updateSuccessTask($this->task, $this->manual, $this->beginTime, $this->totalSent, $this->totalFailed, $this->sendIds);
        }


    }

    /**
     * Combines the receivers and body data into an iterator.
     *
     * This method combines the provided receivers and body arrays into an iterator that can be iterated over
     * simultaneously, returning a pair of receiver and body for each iteration.
     *
     * @tag srm_schdular
     */
    private function getEmailInfoIterator($receivers, $body)
    {
        $mi = new MultipleIterator();
        $mi->attachIterator(new ArrayIterator($receivers));
        $mi->attachIterator(new ArrayIterator($body));

        return $mi;
    }


    /**
     * Updates the task if it is manually triggered.
     *
     * This method checks if the task was triggered manually and if the provided data is an array. If both conditions
     * are met, the task is updated with the provided data.
     *
     * @tag srm_schdular
     */
    private function updateIfManual($task, $data)
    {
        if($this->manual && is_array($data)) {
            $task->update($data);
        }
    }

    /**
     * Checks if the SMTP configuration exists in the system.
     *
     * This method checks if there is a valid SMTP configuration in the system. If no configuration is found,
     * it logs an error and throws an exception.
     *
     * @tag srm_schdular
     */
    private function checkSMTPExist()
    {
        $smtp = SMTPMails::defaultSmtpMail()->first();

        if(is_null($smtp)) {
            Log::error('SMTP is not exist');
            throw new \Exception('SMTP is not exist');
        }
    }

    /**
     * Handles task failure and updates the task status accordingly.
     *
     * This method is called when the task fails. It updates the task status to `failed`, logs the failure details,
     * and records the exception information.
     *
     * @tag srm_schdular
     */
    public function failed(\Throwable $exception)
    {
        updateFailedTask(
            $this->task,
            $this->manual,
            $exception,
            $this->beginTime,
            $this->totalSent,
            $this->totalFailed,
            $this->sendIds,
            'failed'
        );
    }


    /**
     * Performs cleanup actions after task completion.
     *
     * This method is called after the task is completed, either successfully or with failure. It performs necessary
     * cleanup actions, such as deleting any task-related attachments.
     *
     * @tag srm_schdular
     */
    public function after()
    {
        deleteTaskAttachment($this->task);
    }



}
