<?php

namespace App\Jobs;

use App\Mail\CertificateReadyMail;
use App\Models\Certificate;
use App\Models\EmailLog;
use App\Models\Program;
use App\Models\ProgramParticipant;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Mail;

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

    public int $tries   = 3;
    public int $backoff = 60;

    public function __construct(public readonly Certificate $certificate) {}

    public function handle(): void
    {
        $cert = $this->certificate->refresh();
        $cert->load(['participant', 'program']);

        $email = $cert->participant->email;

        if (! $email) {
            $this->updatePpStatus($cert, null);
            EmailLog::where('certificate_id', $cert->id)->where('status', 'pending')->delete();
            return;
        }

        $log = EmailLog::where('certificate_id', $cert->id)
            ->where('status', 'pending')
            ->latest()
            ->first();

        try {
            Mail::to($email)->send(new CertificateReadyMail($cert));

            $cert->update(['status' => 'emailed', 'emailed_at' => now()]);

            $this->updatePpStatus($cert, 'sent');

            if ($log) {
                $log->update(['status' => 'sent', 'sent_at' => now()]);
            } else {
                EmailLog::create([
                    'program_id'      => $cert->program_id,
                    'participant_id'  => $cert->participant_id,
                    'certificate_id'  => $cert->id,
                    'recipient_email' => $email,
                    'subject'         => 'Sijil Digital Program — ' . $cert->program->title,
                    'email_type'      => 'certificate_ready',
                    'status'          => 'sent',
                    'sent_at'         => now(),
                ]);
            }
        } catch (\Throwable $e) {
            $this->updatePpStatus($cert, 'failed');

            if ($log) {
                $log->update(['status' => 'failed', 'error_message' => $e->getMessage()]);
            } else {
                EmailLog::create([
                    'program_id'      => $cert->program_id,
                    'participant_id'  => $cert->participant_id,
                    'certificate_id'  => $cert->id,
                    'recipient_email' => $email,
                    'subject'         => 'Sijil Digital Program — ' . $cert->program->title,
                    'email_type'      => 'certificate_ready',
                    'status'          => 'failed',
                    'error_message'   => $e->getMessage(),
                ]);
            }

            throw $e;
        }
    }

    /**
     * Cipta pending EmailLog dan set status_sent_emel = pending, kemudian dispatch job.
     */
    public static function dispatchForCert(Certificate $cert): void
    {
        $cert->loadMissing(['participant', 'program']);

        self::updatePpStatusStatic($cert, 'pending');

        EmailLog::create([
            'program_id'      => $cert->program_id,
            'participant_id'  => $cert->participant_id,
            'certificate_id'  => $cert->id,
            'recipient_email' => $cert->participant->email,
            'subject'         => 'Sijil Digital Program — ' . $cert->program->title,
            'email_type'      => 'certificate_ready',
            'status'          => 'pending',
        ]);

        static::dispatch($cert);
    }

    public static function dispatchBatch(Program $program): void
    {
        $program->certificates()
            ->whereIn('status', ['generated'])
            ->whereNull('emailed_at')
            ->with(['participant', 'program'])
            ->each(function (Certificate $cert) {
                if ($cert->participant->email) {
                    static::dispatchForCert($cert);
                }
            });
    }

    private function updatePpStatus(Certificate $cert, ?string $status): void
    {
        ProgramParticipant::where('program_id', $cert->program_id)
            ->where('participant_id', $cert->participant_id)
            ->update(['status_sent_emel' => $status]);
    }

    private static function updatePpStatusStatic(Certificate $cert, ?string $status): void
    {
        ProgramParticipant::where('program_id', $cert->program_id)
            ->where('participant_id', $cert->participant_id)
            ->update(['status_sent_emel' => $status]);
    }
}
