<?php
/**
 * webhook.php  —  the single URL you paste into Channels -> Webhook.
 *
 * GET  = one-time verification handshake, must echo the challenge back raw.
 * POST = every incoming message / delivery report from then on.
 *
 * IMPORTANT: no blank line, no BOM, no HTML before "<?php" in this file.
 */

require_once __DIR__ . '/config.php';
require_once __DIR__ . '/wa_api.php';

// =====================================================================
// 1. VERIFICATION HANDSHAKE
// =====================================================================
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
    // Their panel spells it "challange". Meta spells it "hub.challenge".
    // Accept all spellings so the same file works either way.
    foreach (['challange', 'challenge', 'hub_challenge'] as $key) {
        if (isset($_GET[$key])) {
            header('Content-Type: text/html; charset=utf-8');
            http_response_code(200);
            echo $_GET[$key];   // raw echo, nothing else on the page
            exit;
        }
    }
    http_response_code(200);
    echo 'no challange';
    exit;
}

// =====================================================================
// 2. EVENT INTAKE  (POST)
// =====================================================================
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    http_response_code(405);
    exit;
}

$raw     = file_get_contents('php://input');
$headers = function_exists('getallheaders') ? getallheaders() : [];
$rawId   = 0;

// Store first, think later. If parsing blows up you still have the payload.
try {
    $stmt = db()->prepare(
        "INSERT INTO wa_webhook_raw (ip, headers, payload) VALUES (?, ?, ?)"
    );
    $stmt->execute([
        $_SERVER['REMOTE_ADDR'] ?? '',
        json_encode($headers, JSON_UNESCAPED_UNICODE),
        $raw,
    ]);
    $rawId = (int) db()->lastInsertId();
} catch (Throwable $e) {
    error_log('wa webhook store failed: ' . $e->getMessage());
}

// Acknowledge immediately — providers retry or disable slow endpoints.
http_response_code(200);
header('Content-Type: application/json');
echo '{"status":"ok"}';

if (function_exists('fastcgi_finish_request')) {
    fastcgi_finish_request();          // php-fpm: close connection, keep running
} else {
    ignore_user_abort(true);
    @ob_end_flush();
    @flush();
}

// Optional shared-secret gate (applies to POST only).
if (WA_WEBHOOK_SECRET !== '' && (($_GET['s'] ?? '') !== WA_WEBHOOK_SECRET)) {
    wa_mark_raw($rawId, 'rejected: bad secret');
    exit;
}

// =====================================================================
// 3. PROCESSING
// =====================================================================
try {
    $data = json_decode($raw, true);
    if (!is_array($data)) {
        $data = $_POST;                // some panels post form-encoded instead
    }

    $events = wa_normalize($data);

    if (!$events) {
        wa_mark_raw($rawId, 'unrecognised payload shape - inspect wa_webhook_raw.payload');
        exit;
    }

    foreach ($events as $e) {
        if ($e['kind'] === 'message') {
            wa_handle_incoming($e, $rawId);
        } else {
            wa_handle_status($e);
        }
    }

    wa_mark_raw($rawId, null, 1);
} catch (Throwable $e) {
    error_log('wa webhook process failed: ' . $e->getMessage());
    wa_mark_raw($rawId, $e->getMessage());
}
exit;


// =====================================================================
// Helpers
// =====================================================================

function wa_mark_raw(int $id, ?string $error, int $processed = 0): void
{
    if ($id <= 0) return;
    try {
        db()->prepare("UPDATE wa_webhook_raw SET processed = ?, error = ? WHERE id = ?")
            ->execute([$processed, $error, $id]);
    } catch (Throwable $e) {
        error_log($e->getMessage());
    }
}

/**
 * Flattens whatever shape arrived into a simple list of events.
 * Handles the Meta Cloud API envelope and the flat style most resellers use.
 */
function wa_normalize(array $d): array
{
    $out = [];

    // --- Shape A: Meta Cloud API envelope ---
    if (isset($d['entry']) && is_array($d['entry'])) {
        foreach ($d['entry'] as $entry) {
            foreach ($entry['changes'] ?? [] as $change) {
                $v = $change['value'] ?? [];

                foreach ($v['messages'] ?? [] as $m) {
                    $out[] = [
                        'kind'  => 'message',
                        'id'    => $m['id'] ?? '',
                        'from'  => $m['from'] ?? '',
                        'to'    => $v['metadata']['display_phone_number'] ?? '',
                        'type'  => $m['type'] ?? 'text',
                        'text'  => wa_extract_text($m),
                        'media' => $m['image']['id'] ?? $m['document']['id'] ?? null,
                        'time'  => isset($m['timestamp']) ? (int) $m['timestamp'] : time(),
                    ];
                }

                foreach ($v['statuses'] ?? [] as $s) {
                    $out[] = [
                        'kind'   => 'status',
                        'id'     => $s['id'] ?? '',
                        'status' => $s['status'] ?? '',
                        'to'     => $s['recipient_id'] ?? '',
                        'reason' => $s['errors'][0]['title'] ?? null,
                        'time'   => isset($s['timestamp']) ? (int) $s['timestamp'] : time(),
                    ];
                }
            }
        }
        if ($out) return $out;
    }

    // --- Shape B: flat reseller payload ---
    $type = strtolower((string) ($d['type'] ?? $d['event'] ?? $d['event_type'] ?? ''));

    if (strpos($type, 'status') !== false || isset($d['status'])) {
        $out[] = [
            'kind'   => 'status',
            'id'     => $d['message_id'] ?? $d['id'] ?? '',
            'status' => $d['status'] ?? '',
            'to'     => $d['to'] ?? $d['recipient'] ?? '',
            'reason' => $d['error'] ?? $d['reason'] ?? null,
            'time'   => time(),
        ];
    } elseif (isset($d['from']) || isset($d['sender'])) {
        $out[] = [
            'kind'  => 'message',
            'id'    => $d['message_id'] ?? $d['id'] ?? uniqid('in_', true),
            'from'  => $d['from'] ?? $d['sender'] ?? '',
            'to'    => $d['to'] ?? '',
            'type'  => $d['message_type'] ?? 'text',
            'text'  => $d['text'] ?? $d['message'] ?? $d['body'] ?? '',
            'media' => $d['media_url'] ?? null,
            'time'  => time(),
        ];
    }

    return $out;
}

function wa_extract_text(array $m): string
{
    return $m['text']['body']
        ?? $m['button']['text']
        ?? $m['interactive']['button_reply']['title']
        ?? $m['interactive']['list_reply']['title']
        ?? $m['caption']
        ?? '';
}

function wa_handle_incoming(array $e, int $rawId): void
{
    $stmt = db()->prepare(
        "INSERT INTO wa_messages
           (wa_message_id, direction, wa_from, wa_to, msg_type, body, media_url, status, event_time, raw_id)
         VALUES (?, 'in', ?, ?, ?, ?, ?, 'received', FROM_UNIXTIME(?), ?)
         ON DUPLICATE KEY UPDATE id = id"   // duplicate retry: ignored silently
    );
    $stmt->execute([
        $e['id'], $e['from'], $e['to'], $e['type'],
        $e['text'], $e['media'], $e['time'], $rawId,
    ]);

    // rowCount() is 0 when it was a duplicate -> don't reply twice.
    if ($stmt->rowCount() === 0) return;

    if (WA_AUTOREPLY_ENABLED) {
        $reply = wa_build_reply($e['text'], $e['from']);
        if ($reply !== null && $e['from'] !== '') {
            wa_send_text($e['from'], $reply);
        }
    }
}

function wa_handle_status(array $e): void
{
    db()->prepare(
        "INSERT INTO wa_status_log (wa_message_id, status, reason, event_time)
         VALUES (?, ?, ?, FROM_UNIXTIME(?))"
    )->execute([$e['id'], $e['status'], $e['reason'], $e['time']]);

    db()->prepare(
        "UPDATE wa_messages SET status = ? WHERE wa_message_id = ? AND direction = 'out'"
    )->execute([$e['status'], $e['id']]);
}

/**
 * Your business logic lives here. Return null to stay silent.
 */
function wa_build_reply(string $text, string $from): ?string
{
    $t = strtolower(trim($text));

    if ($t === '') return null;

    if (in_array($t, ['hi', 'hello', 'hey', 'start'], true)) {
        return "Hello! Reply with:\n1 - Order status\n2 - Talk to support";
    }
    if ($t === '1') {
        // Example: pull real data from your own tables
        $row = db()->prepare("SELECT status FROM orders WHERE phone = ? ORDER BY id DESC LIMIT 1");
        $row->execute([$from]);
        $order = $row->fetch();
        return $order
            ? 'Your latest order is: ' . $order['status']
            : 'We could not find an order for this number.';
    }
    if ($t === '2') {
        return 'Sure — an agent will message you shortly.';
    }

    return "Sorry, I didn't understand that. Reply HI for the menu.";
}
