<?php
/**
 * WhatsApp Cloud API integration (Meta Graph API) for EduTrack.
 *
 * Mirrors the proven builerz flow: text + template messages, a 24-hour
 * customer-service window with a pending queue, document delivery for report
 * cards, and full send logging. Credentials come from the settings table
 * (admin-editable) with .env fallback, matching how email/SMS are configured.
 *
 * Gated by the whatsapp_comms add-on at the page/AJAX layer; the low-level
 * senders themselves are dependency-free so cron/AI actions can reuse them.
 */

if (!function_exists('wa_config')) {

    /** Read a WhatsApp config value: settings table first, then .env fallback. */
    function wa_config(string $key, string $default = ''): string
    {
        static $envMap = [
            'wa_token'        => 'META_WA_TOKEN',
            'wa_phone_id'     => 'META_WA_PHONE_ID',
            'wa_verify_token' => 'WA_VERIFY_TOKEN',
            'wa_api_version'  => 'META_WA_API_VERSION',
        ];
        if (function_exists('getSettingValue')) {
            $v = getSettingValue($key);
            if ($v !== null && $v !== '') return $v;
        }
        $envKey = $envMap[$key] ?? null;
        if ($envKey && !empty($_ENV[$envKey])) return (string) $_ENV[$envKey];
        return $default;
    }

    /** True when a token + phone-number id are present. */
    function wa_is_configured(): bool
    {
        return wa_config('wa_token') !== '' && wa_config('wa_phone_id') !== '';
    }

    /** Ensure the WhatsApp bookkeeping tables exist. */
    function wa_ensure_tables(mysqli $conn): void
    {
        static $done = false;
        if ($done) return;
        $done = true;
        $conn->query("CREATE TABLE IF NOT EXISTS whatsapp_log (
            id INT AUTO_INCREMENT PRIMARY KEY,
            to_number VARCHAR(20) NOT NULL,
            message_type VARCHAR(20) NOT NULL,
            template_name VARCHAR(80) NULL,
            message_preview VARCHAR(500) NULL,
            message_id VARCHAR(120) NULL,
            status VARCHAR(12) NOT NULL,
            error_message VARCHAR(255) NULL,
            sent_by INT NULL,
            sent_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            KEY idx_to (to_number), KEY idx_sent (sent_at)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci");
        $conn->query("CREATE TABLE IF NOT EXISTS whatsapp_sessions (
            phone VARCHAR(20) PRIMARY KEY,
            last_inbound_at DATETIME NOT NULL
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci");
        $conn->query("CREATE TABLE IF NOT EXISTS whatsapp_pending (
            id INT AUTO_INCREMENT PRIMARY KEY,
            phone VARCHAR(20) NOT NULL,
            body TEXT NOT NULL,
            subject VARCHAR(160) NULL,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            sent_at DATETIME NULL,
            KEY idx_phone (phone)
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci");
    }

    /** Normalise a Ghana number to E.164 (+233…). Reuses formatPhoneNumber when present. */
    function wa_format_phone(string $phone): string
    {
        $digits = preg_replace('/\D+/', '', $phone);
        if ($digits === '') return '';
        if (strpos($digits, '233') === 0)      $local = substr($digits, 3);
        elseif ($digits[0] === '0')            $local = substr($digits, 1);
        else                                   $local = $digits;
        $local = ltrim($local, '0');
        return '+233' . $local;
    }

    /** Low-level Graph API POST. Returns ['ok','message_id','error','http']. */
    function wa_request(array $payload): array
    {
        $token   = wa_config('wa_token');
        $phoneId = wa_config('wa_phone_id');
        $ver     = wa_config('wa_api_version', 'v19.0');
        if ($token === '' || $phoneId === '') {
            return ['ok' => false, 'message_id' => null, 'error' => 'WhatsApp not configured.', 'http' => 0];
        }
        $url = "https://graph.facebook.com/{$ver}/{$phoneId}/messages";
        $ch  = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_POST           => true,
            CURLOPT_HTTPHEADER     => ['Authorization: Bearer ' . $token, 'Content-Type: application/json'],
            CURLOPT_POSTFIELDS     => json_encode($payload),
            CURLOPT_TIMEOUT        => 25,
        ]);
        $raw  = curl_exec($ch);
        $http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $cerr = curl_error($ch);
        curl_close($ch);
        if ($raw === false) return ['ok' => false, 'message_id' => null, 'error' => 'cURL: ' . $cerr, 'http' => $http];
        $data = json_decode($raw, true);
        if ($http >= 200 && $http < 300 && isset($data['messages'][0]['id'])) {
            return ['ok' => true, 'message_id' => $data['messages'][0]['id'], 'error' => null, 'http' => $http];
        }
        return ['ok' => false, 'message_id' => null, 'error' => $data['error']['message'] ?? ('HTTP ' . $http), 'http' => $http];
    }

    /** Record a send to whatsapp_log (best-effort). */
    function wa_log(string $to, string $type, ?string $template, string $preview, array $result): void
    {
        try {
            $conn = getDBConnection();
            wa_ensure_tables($conn);
            $status = $result['ok'] ? 'sent' : 'failed';
            $mid    = $result['message_id'] ?? null;
            $err    = $result['error'] ?? null;
            $by     = $_SESSION['user_id'] ?? null;
            $prev   = mb_substr($preview, 0, 500);
            $stmt = $conn->prepare("INSERT INTO whatsapp_log
                (to_number, message_type, template_name, message_preview, message_id, status, error_message, sent_by)
                VALUES (?,?,?,?,?,?,?,?)");
            $stmt->bind_param('sssssssi', $to, $type, $template, $prev, $mid, $status, $err, $by);
            $stmt->execute(); $stmt->close();
        } catch (Throwable $e) {
            error_log('wa_log: ' . $e->getMessage());
        }
    }

    /** WhatsApp bold uses *single* asterisks; strip Markdown Meta rejects (err 132018). */
    function wa_sanitize(string $text, int $max = 950): string
    {
        $text = preg_replace('/\*\*([^*\n]+)\*\*/', '*$1*', $text);
        $text = str_replace('**', '', $text);
        $text = preg_replace('/^#{1,6}\s+/m', '', $text);
        $text = str_replace(["\r\n", "\r"], "\n", $text);
        $text = preg_replace('/\n{3,}/', "\n\n", trim($text));
        if (mb_strlen($text) > $max) {
            $cut = mb_substr($text, 0, $max);
            $sp  = mb_strrpos($cut, ' ');
            $text = mb_substr($cut, 0, $sp ?: $max) . "\n\n_[See full details in the school system]_";
        }
        return $text;
    }

    /** Flatten a string for use as a template parameter (no newlines/tabs/4-space runs). */
    function wa_template_param(string $s, int $max = 960): string
    {
        $s = str_replace(["\r\n", "\r"], "\n", $s);
        $s = str_replace("\t", ' ', $s);
        $s = preg_replace('/\n{2,}/', "\n", $s);
        $s = preg_replace('/ {2,}/', ' ', $s);
        $s = str_replace("\n", ' / ', trim($s));
        return mb_substr($s, 0, $max);
    }

    /** Send a free-form text message (only valid inside the 24h window). */
    function send_whatsapp_text(string $to, string $message): array
    {
        $to = wa_format_phone($to);
        $r  = wa_request([
            'messaging_product' => 'whatsapp',
            'recipient_type'    => 'individual',
            'to'                => $to,
            'type'              => 'text',
            'text'              => ['body' => $message, 'preview_url' => false],
        ]);
        wa_log($to, 'text', null, $message, $r);
        return ['success' => $r['ok'], 'message_id' => $r['message_id'], 'error' => $r['error']];
    }

    /** Send a template message (for business-initiated conversations). */
    function send_whatsapp_template(string $to, string $template, array $params = [], string $lang = 'en'): array
    {
        $to = wa_format_phone($to);
        $components = [];
        if (!empty($params)) {
            $components[] = [
                'type'       => 'body',
                'parameters' => array_map(fn($p) => ['type' => 'text', 'text' => wa_template_param((string) $p)], $params),
            ];
        }
        $r = wa_request([
            'messaging_product' => 'whatsapp',
            'to'                => $to,
            'type'              => 'template',
            'template'          => ['name' => $template, 'language' => ['code' => $lang], 'components' => $components],
        ]);
        wa_log($to, 'template', $template, $template . ': ' . implode(' | ', $params), $r);
        return ['success' => $r['ok'], 'message_id' => $r['message_id'], 'error' => $r['error']];
    }

    /**
     * Send a document (e.g. a terminal-report PDF). $mediaUrl must be a publicly
     * reachable https URL, or pass an already-uploaded $mediaId.
     */
    function send_whatsapp_document(string $to, string $mediaUrl, string $filename, string $caption = '', ?string $mediaId = null): array
    {
        $to  = wa_format_phone($to);
        $doc = $mediaId ? ['id' => $mediaId] : ['link' => $mediaUrl];
        $doc['filename'] = $filename;
        if ($caption !== '') $doc['caption'] = mb_substr($caption, 0, 1000);
        $r = wa_request([
            'messaging_product' => 'whatsapp',
            'to'                => $to,
            'type'              => 'document',
            'document'          => $doc,
        ]);
        wa_log($to, 'document', null, $filename . ($caption ? (' — ' . $caption) : ''), $r);
        return ['success' => $r['ok'], 'message_id' => $r['message_id'], 'error' => $r['error']];
    }

    // ── 24-hour customer-service window ────────────────────────────────────────

    function wa_record_inbound(string $phone): void
    {
        $phone = wa_format_phone($phone);
        if ($phone === '') return;
        try {
            $conn = getDBConnection(); wa_ensure_tables($conn);
            $stmt = $conn->prepare("INSERT INTO whatsapp_sessions (phone, last_inbound_at) VALUES (?, NOW())
                                    ON DUPLICATE KEY UPDATE last_inbound_at = NOW()");
            $stmt->bind_param('s', $phone); $stmt->execute(); $stmt->close();
        } catch (Throwable $e) { error_log('wa_record_inbound: ' . $e->getMessage()); }
    }

    function wa_window_open(string $phone): bool
    {
        $phone = wa_format_phone($phone);
        if ($phone === '') return false;
        try {
            $conn = getDBConnection(); wa_ensure_tables($conn);
            $stmt = $conn->prepare("SELECT 1 FROM whatsapp_sessions WHERE phone=? AND last_inbound_at > (NOW() - INTERVAL 24 HOUR)");
            $stmt->bind_param('s', $phone); $stmt->execute();
            $open = (bool) $stmt->get_result()->fetch_row(); $stmt->close();
            return $open;
        } catch (Throwable $e) { return false; }
    }

    function wa_queue_pending(string $phone, string $body, ?string $subject = null): void
    {
        $phone = wa_format_phone($phone);
        if ($phone === '' || trim($body) === '') return;
        try {
            $conn = getDBConnection(); wa_ensure_tables($conn);
            $del = $conn->prepare("DELETE FROM whatsapp_pending WHERE phone=? AND sent_at IS NULL AND body=?");
            $del->bind_param('ss', $phone, $body); $del->execute(); $del->close();
            $ins = $conn->prepare("INSERT INTO whatsapp_pending (phone, body, subject) VALUES (?,?,?)");
            $ins->bind_param('sss', $phone, $body, $subject); $ins->execute(); $ins->close();
        } catch (Throwable $e) { error_log('wa_queue_pending: ' . $e->getMessage()); }
    }

    /** Flush queued free-form messages for a number whose window just opened. */
    function wa_flush_pending(string $phone): int
    {
        $phone = wa_format_phone($phone);
        if ($phone === '') return 0;
        $sent = 0;
        try {
            $conn = getDBConnection(); wa_ensure_tables($conn);
            $st = $conn->prepare("SELECT id, body FROM whatsapp_pending
                WHERE phone=? AND sent_at IS NULL AND created_at > (NOW() - INTERVAL 48 HOUR) ORDER BY id ASC");
            $st->bind_param('s', $phone); $st->execute();
            $rows = $st->get_result()->fetch_all(MYSQLI_ASSOC); $st->close();
            foreach ($rows as $row) {
                $r = send_whatsapp_text($phone, $row['body']);
                if (!empty($r['success'])) {
                    $conn->query("UPDATE whatsapp_pending SET sent_at=NOW() WHERE id=" . (int) $row['id']);
                    $sent++;
                }
            }
            $ex = $conn->prepare("UPDATE whatsapp_pending SET sent_at=NOW()
                WHERE phone=? AND sent_at IS NULL AND created_at <= (NOW() - INTERVAL 48 HOUR)");
            $ex->bind_param('s', $phone); $ex->execute(); $ex->close();
        } catch (Throwable $e) { error_log('wa_flush_pending: ' . $e->getMessage()); }
        return $sent;
    }

    /**
     * High-level convenience wrapper mirroring sendSMS()/sendEmail().
     * Inside the 24h window it sends the full text; otherwise, if $template is
     * supplied it sends that (template params = [$message]), and always queues the
     * full text for delivery when the window next opens.
     */
    function sendWhatsApp(string $phone, string $message, ?string $template = null, array $templateParams = []): array
    {
        if (defined('COMMS_DRY_RUN') && COMMS_DRY_RUN) {
            error_log('[DRY RUN] WhatsApp suppressed to ' . $phone . ': ' . substr($message, 0, 80));
            return ['success' => true, 'dry_run' => true];
        }
        $clean = wa_sanitize($message);
        if (wa_window_open($phone)) {
            return send_whatsapp_text($phone, $clean);
        }
        if ($template) {
            wa_queue_pending($phone, $clean);
            return send_whatsapp_template($phone, $template, $templateParams ?: [$clean]);
        }
        // No window and no template: best-effort text (will fail outside window) + queue.
        wa_queue_pending($phone, $clean);
        return send_whatsapp_text($phone, $clean);
    }

    /** Parse the configured report-recipients setting into an array of numbers. */
    function wa_report_recipients(): array
    {
        $raw = wa_config('wa_report_recipients');
        if (trim($raw) === '') return [];
        return array_values(array_filter(array_map('trim', preg_split('/[,\n;]+/', $raw))));
    }
}
