<?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),
                        'reply_id' => wa_extract_reply_id($m),   // list/button tap
                        'order'    => $m['order'] ?? null,       // catalog checkout
                        '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'] ?? '',
            'reply_id' => $d['reply_id'] ?? null,
            'order'    => $d['order'] ?? null,
            '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']
        ?? '';
}

/**
 * The stable identifier behind a tapped button or list row.
 * ALWAYS route on this, never on the visible title.
 */
function wa_extract_reply_id(array $m): ?string
{
    return $m['interactive']['list_reply']['id']
        ?? $m['interactive']['button_reply']['id']
        ?? $m['button']['payload']
        ?? null;
}

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 && $e['from'] !== '') {
        wa_route($e);
    }
}

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']]);
}

/**
 * ROUTER — your business logic lives here.
 *
 * Called for every genuinely new inbound message. Decide what to send back
 * (or send nothing). Route on $e['reply_id'] when the customer tapped a
 * button or list row; fall back to text for typed messages.
 */
function wa_route(array $e): void
{
    $from = $e['from'];
    $id   = $e['reply_id'] ?? null;
    $text = strtolower(trim($e['text'] ?? ''));

    // --- 1. Catalog checkout: customer sent a cart ---
    if (!empty($e['order']['product_items'])) {
        $total = 0.0;
        $lines = [];
        foreach ($e['order']['product_items'] as $item) {
            $qty   = (int) ($item['quantity'] ?? 1);
            $price = (float) ($item['item_price'] ?? 0);
            $total += $qty * $price;
            $lines[] = $qty . ' x ' . ($item['product_retailer_id'] ?? '?');
        }
        wa_send_text($from,
            "Thanks for your order:\n" . implode("\n", $lines) .
            "\n\nTotal: " . number_format($total, 2) . "\nWe'll confirm shortly.");
        return;
    }

    // --- 2. Tapped a list row or button: route on the ID ---
    if ($id !== null) {
        switch ($id) {

            case 'menu_products':
                wa_send_list(
                    $from,
                    'Here is what we have in stock. Tap an item for details.',
                    'View products',
                    [
                        'Shirts' => [
                            ['id' => 'sku_shirt_cotton', 'title' => 'Cotton Shirt',  'description' => 'Rs. 499 — S/M/L/XL'],
                            ['id' => 'sku_shirt_linen',  'title' => 'Linen Shirt',   'description' => 'Rs. 899 — M/L only'],
                        ],
                        'Trousers' => [
                            ['id' => 'sku_trouser_form', 'title' => 'Formal Trouser', 'description' => 'Rs. 749'],
                        ],
                    ],
                    'Our Catalogue',
                    'Prices include GST'
                );
                return;

            case 'menu_address':
                // Replace with your real shop coordinates.
                wa_send_location(
                    $from,
                    23.022505, 72.571365,
                    'Our Store',
                    '123 Example Road, Ahmedabad, Gujarat 380001'
                );
                return;

            case 'menu_support':
                wa_send_text($from, 'An agent will message you shortly.');
                return;
        }

        // Any product SKU tapped from the list above
        if (strpos($id, 'sku_') === 0) {
            $stmt = db()->prepare("SELECT name, price, stock FROM products WHERE sku = ? LIMIT 1");
            $stmt->execute([$id]);
            $p = $stmt->fetch();

            wa_send_text($from, $p
                ? $p['name'] . "\nPrice: Rs. " . $p['price'] .
                  "\n" . ($p['stock'] > 0 ? 'In stock' : 'Out of stock') .
                  "\n\nReply BUY to order."
                : 'Sorry, that item is no longer listed.');
            return;
        }

        wa_send_text($from, "Sorry, I didn't recognise that option.");
        return;
    }

    // --- 3. Typed text: show the main menu ---
    if (in_array($text, ['hi', 'hello', 'hey', 'start', 'menu'], true)) {
        wa_send_buttons(
            $from,
            'Welcome! What would you like to do?',
            [
                'menu_products' => 'See products',
                'menu_address'  => 'Our address',
                'menu_support'  => 'Talk to us',
            ],
            'Main Menu'
        );
        return;
    }

    if ($text !== '') {
        wa_send_text($from, "Sorry, I didn't understand that. Reply MENU for options.");
    }
}
