<?php

namespace App\Listeners;

use App\Models\WhatsappInviteLogModel;

// QR Code (version-agnostic usage)
use Endroid\QrCode\Builder\Builder;
use Endroid\QrCode\Encoding\Encoding;
use Endroid\QrCode\Writer\PngWriter;

class WhatsappInviteListener
{
    protected $emailService;
    protected WhatsappInviteLogModel $logModel;

    public function __construct($emailService)
    {
        if (!$emailService || !method_exists($emailService, 'send')) {
            throw new \RuntimeException('WhatsappInviteListener needs a valid EmailService with send().');
        }
        $this->emailService = $emailService;
        $this->logModel     = new WhatsappInviteLogModel();
    }

    public function handle(array $payload): void
    {
        // ===== recipients =====
        $to = array_values(array_unique(array_filter((array) ($payload['to_emails'] ?? []))));
        $cc = array_values(array_unique(array_filter((array) ($payload['cc_emails'] ?? []))));
        // ensure CC doesn’t duplicate TO
        $cc = array_values(array_diff($cc, $to));

        // if TO is empty but CC exists, promote CC -> TO
        if (empty($to) && !empty($cc)) {
            $to = $cc;
            $cc = [];
        }
        if (empty($to)) {
            log_message('debug', 'WA listener: no recipients after normalization; abort.');
            return;
        }

        log_message('info', 'WA listener invoked. TO={to} CC={cc}', [
            'to' => implode(',', $to),
            'cc' => implode(',', $cc),
        ]);

        // ===== inputs =====
        $sections   = $payload['sections']   ?? [];
        $links      = array_values(array_unique(array_filter((array) ($payload['links'] ?? []))));
        $parent     = $payload['parent']     ?? null;
        $schoolYear = $payload['schoolYear'] ?? null;
        $semester   = $payload['semester']   ?? null;

        // ===== build items (section/link + QR) =====
        $seen  = [];
        $items = [];

        // Prefer structured sections
        foreach ($sections as $s) {
            $sid  = isset($s['class_section_id']) ? (int) $s['class_section_id'] : 0;
            $name = trim($s['class_section_name'] ?? ($sid ? ('Section ' . $sid) : 'Class'));
            $url  = trim((string) ($s['invite_link'] ?? ''));

            if ($url === '') continue;
            $key = $sid . '|' . $url;
            if (isset($seen[$key])) continue;
            $seen[$key] = true;

            $qrSrc = '';
            try {
                $qrSrc = method_exists($this, 'generateQrImage') ? $this->generateQrImage($url) : '';
            } catch (\Throwable $e) {
                log_message('error', 'QR generation failed for section {sid}: {err}', ['sid' => $sid, 'err' => $e->getMessage()]);
            }

            $items[] = [
                'class_section_id'   => $sid,
                'class_section_name' => $name,
                'invite_link'        => $url,
                'qr_src'             => $qrSrc,
            ];
        }

        // Fallback: raw links
        if (empty($items) && !empty($links)) {
            foreach ($links as $i => $url) {
                $url = trim((string) $url);
                if ($url === '') continue;
                $key = '0|' . $url;
                if (isset($seen[$key])) continue;
                $seen[$key] = true;

                $qrSrc = '';
                try {
                    $qrSrc = method_exists($this, 'generateQrImage') ? $this->generateQrImage($url) : '';
                } catch (\Throwable $e) {
                    log_message('error', 'QR generation failed for link: {err}', ['err' => $e->getMessage()]);
                }

                $items[] = [
                    'class_section_id'   => 0,
                    'class_section_name' => 'Class Link ' . ($i + 1),
                    'invite_link'        => $url,
                    'qr_src'             => $qrSrc,
                ];
            }
        }

        if (empty($items)) {
            log_message('debug', 'WA listener: built 0 items; abort.');
            return;
        }

        // ===== subject & view =====
        $subject = count($items) > 1
            ? 'Your WhatsApp Group Links (Multiple Classes)'
            : 'Your WhatsApp Group Link';

        $viewData = [
            'parent'       => $parent,
            'items'        => $items,
            'sections'     => $sections,   // legacy fallback
            'links'        => $links,      // legacy fallback
            'schoolYear'   => $schoolYear,
            'semester'     => $semester,
            'title'        => $subject,
            'teacherNames' => $payload['teachers'] ?? [],
        ];

        try {
            $bodyHtml = view('emails/whatsapp_invite', $viewData, ['saveData' => true]);
        } catch (\Throwable $e) {
            // very small plain fallback
            $lines = ['Assalamu Alaikum,', '', 'Here are your WhatsApp link(s):'];
            foreach ($items as $it) {
                $lines[] = ' - ' . $it['class_section_name'] . ': ' . $it['invite_link'];
            }
            $lines[] = '';
            $lines[] = 'Jazākumu-llāhu khayran.';
            $bodyHtml = nl2br(implode("\n", $lines));
        }

        $singleSectionId = (count($items) === 1)
            ? ((int) ($items[0]['class_section_id'] ?? 0) ?: null)
            : null;

        // ===== send once per family: TO + CC =====
        try {
            $email = \Config\Services::email();

            // Ensure From is set (many SMTP servers reject otherwise)
            $cfg = config('Email');
            $fromEmail = $cfg->fromEmail ?? getenv('email.fromEmail') ?: 'no-reply@example.com';
            $fromName  = $cfg->fromName  ?? getenv('email.fromName')  ?: 'Al Rahma Sunday School';
            $email->setFrom($fromEmail, $fromName);

            // HTML + proper newlines for SMTP
            $email->setMailType('html');
            $email->setCRLF("\r\n");
            $email->setNewline("\r\n");

            $email->setTo($to);
            if (!empty($cc)) $email->setCC($cc);
            $email->setSubject($subject);
            $email->setMessage($bodyHtml);

            $ok = $email->send();

            if ($ok) {
                log_message('info', 'WhatsApp invite SENT to=' . implode(', ', $to) . (empty($cc) ? '' : ' CC=' . implode(', ', $cc)));
                if (property_exists($this, 'logModel') && $this->logModel) {
                    $this->logModel->logSuccess((int) ($parent['id'] ?? 0), implode(', ', $to), $singleSectionId, empty($cc) ? null : implode(', ', $cc));
                }
            } else {
                // Capture why it failed
                $debug = method_exists($email, 'printDebugger') ? $email->printDebugger(['headers', 'subject']) : 'no debug';
                log_message('error', 'WhatsApp invite FAILED. TO=' . implode(', ', $to) . ' CC=' . implode(', ', $cc) . ' :: ' . $debug);
                if (property_exists($this, 'logModel') && $this->logModel) {
                    $this->logModel->logFailure((int) ($parent['id'] ?? 0), implode(', ', $to), 'send() returned false', $singleSectionId, empty($cc) ? null : implode(', ', $cc));
                }
            }
        } catch (\Throwable $e) {
            log_message('error', 'WhatsApp invite exception: {err}', ['err' => $e->getMessage()]);
            if (property_exists($this, 'logModel') && $this->logModel) {
                $this->logModel->logFailure((int) ($parent['id'] ?? 0), implode(', ', $to), $e->getMessage(), $singleSectionId, empty($cc) ? null : implode(', ', $cc));
            }
        }
    }


    /**
     * Generate a QR PNG for $inviteLink under writable/uploads/qrcodes,
     * and return a DATA-URI string to embed in <img src="...">.
     *
     * Tiers:
     *  1) Endroid v5 (Builder)
     *  2) Endroid v3/v4 (QrCode + PngWriter)
     *  3) chillerlan/qrcode (pure PHP fallback)
     *
     * Throws with detailed message if all tiers fail.
     */
    /**
     * Generate a QR PNG for $inviteLink under writable/uploads/qrcodes and return
     * an <img src="...">-ready value: either a data-URI (base64) or a public URL.
     *
     * Order:
     *  1) Endroid v5 (Builder)
     *  2) Endroid v3/v4 (QrCode + PngWriter)
     *  3) chillerlan/php-qrcode
     *  4) Public fallback URL (api.qrserver.com)
     *
     * Logs every step; never throws in production use — always returns a string.
     */
    /**
     * Generate a QR PNG for $inviteLink under writable/uploads/qrcodes and return
     * an <img src="...">-ready value: either a data-URI (base64) or a public URL.
     * No hard class references => no IDE "unknown class" warnings.
     */
    private function generateQrImage(string $inviteLink): string
    {
        if ($inviteLink === '') {
            log_message('error', 'QR: Empty invite link.');
            return 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMB/UbG2SAAAAAASUVORK5CYII=';
        }

        $dir = WRITEPATH . 'uploads' . DIRECTORY_SEPARATOR . 'qrcodes';
        if (!is_dir($dir)) {
            if (!@mkdir($dir, 0775, true) && !is_dir($dir)) {
                log_message('error', 'QR: Cannot create dir: ' . $dir);
                return 'https://api.qrserver.com/v1/create-qr-code/?size=260x260&qzone=2&data=' . rawurlencode($inviteLink);
            }
        }
        if (!is_writable($dir)) {
            log_message('error', 'QR: Dir not writable: ' . $dir);
            return 'https://api.qrserver.com/v1/create-qr-code/?size=260x260&qzone=2&data=' . rawurlencode($inviteLink);
        }

        $filename = 'wa_' . md5($inviteLink) . '.png';
        $path     = $dir . DIRECTORY_SEPARATOR . $filename;

        if (!is_file($path)) {
            $v5BuilderClass = '\\Endroid\\QrCode\\Builder\\Builder';
            $v5WriterClass  = '\\Endroid\\QrCode\\Writer\\PngWriter';
            $v5Encoding     = '\\Endroid\\QrCode\\Encoding\\Encoding';

            $writerClass    = '\\Endroid\\QrCode\\Writer\\PngWriter';
            $qrClass        = '\\Endroid\\QrCode\\QrCode';

            $chlQRCode      = '\\chillerlan\\QRCode\\QRCode';
            $chlQROptions   = '\\chillerlan\\QRCode\\QROptions';
            $chlQRMatrix    = '\\chillerlan\\QRCode\\Data\\QRMatrix';

            $lastErr = null;

            // --- Tier 1: Endroid v5 (Builder)
            if (class_exists($v5BuilderClass) && class_exists($v5WriterClass)) {
                try {
                    $builder = $v5BuilderClass::create()
                        ->writer(new $v5WriterClass())
                        ->data($inviteLink);

                    if (class_exists($v5Encoding)) {
                        $builder->encoding(new $v5Encoding('UTF-8'));
                    }

                    $result = $builder->size(500)->margin(2)->build();
                    $result->saveToFile($path);
                } catch (\Throwable $e) {
                    $lastErr = 'Endroid v5: ' . $e->getMessage();
                    log_message('error', 'QR v5 failed: ' . $e->getMessage());
                }
            }

            // --- Tier 2: Endroid v3/v4
            if (!is_file($path) && class_exists($writerClass)) {
                try {
                    if (class_exists($qrClass) && method_exists($qrClass, 'create')) {
                        $qr = $qrClass::create($inviteLink); // v4
                    } else {
                        $qr = class_exists($qrClass) ? new $qrClass($inviteLink) : null; // v3
                    }
                    if ($qr) {
                        if (method_exists($qr, 'setSize')) {
                            $qr->setSize(500);
                        }
                        if (method_exists($qr, 'setMargin')) {
                            $qr->setMargin(2);
                        }
                        $writer = new $writerClass();
                        $result = $writer->write($qr);
                        if (method_exists($result, 'saveToFile')) {
                            $result->saveToFile($path);
                        } else {
                            $raw = method_exists($result, 'getString') ? $result->getString() : (string)$result;
                            file_put_contents($path, $raw);
                        }
                    }
                } catch (\Throwable $e) {
                    $lastErr = 'Endroid v3/v4: ' . $e->getMessage();
                    log_message('error', 'QR v3/v4 failed: ' . $e->getMessage());
                }
            }

            // --- Tier 3: chillerlan/php-qrcode
            if (!is_file($path) && class_exists($chlQRCode) && class_exists($chlQROptions)) {
                try {
                    $optsArr = [
                        'version'       => 5,
                        'outputType'    => constant($chlQRCode . '::OUTPUT_IMAGE_PNG'),
                        'scale'         => 6,
                        'imageBase64'   => false,
                        'quietzoneSize' => 2,
                    ];
                    if (class_exists($chlQRMatrix) && defined($chlQRMatrix . '::ECC_H')) {
                        $optsArr['eccLevel'] = constant($chlQRMatrix . '::ECC_H');
                    }

                    $opts   = new $chlQROptions($optsArr);
                    $qrcode = new $chlQRCode($opts);
                    $png    = $qrcode->render($inviteLink);
                    file_put_contents($path, $png);
                } catch (\Throwable $e) {
                    $lastErr = 'chillerlan: ' . $e->getMessage();
                    log_message('error', 'QR chillerlan failed: ' . $e->getMessage());
                }
            }

            if (!is_file($path) && $lastErr) {
                log_message('error', 'QR: All local generators failed. Last: ' . $lastErr);
            }
        }

        // Prefer embedding as data-URI (works even if /writable is not public)
        if (is_file($path)) {
            $data = @file_get_contents($path);
            if ($data !== false) {
                return 'data:image/png;base64,' . base64_encode($data);
            }
            log_message('error', 'QR: Could not read PNG at ' . $path);
        }

        // Final: public service fallback so the email still shows a QR
        return 'https://api.qrserver.com/v1/create-qr-code/?size=260x260&qzone=2&data=' . rawurlencode($inviteLink);
    }
}
