<?php
/**
 * Hostiva WhatsApp Inbound Webhook Controller
 * Completes two-way communication:
 *   - GET  : provider verification (Meta hub.challenge)
 *   - POST : inbound messages (Meta Cloud + Twilio), delivery/read status callbacks
 *
 * Inbound text -> logged -> routed through AIConciergeController -> auto-reply sent back out.
 * Mount (no auth) at:  /hostiva/api/webhook/whatsapp[?property_id=N]
 */
require_once __DIR__ . '/../services/WhatsAppService.php';
require_once __DIR__ . '/ai_concierge.php';

class WhatsAppWebhookController {
    private $db;
    private $config;

    public function __construct($db, $config) {
        $this->db = $db;
        $this->config = $config;
    }

    /* ---------- GET: Meta verification handshake ---------- */
    // Meta calls: ?hub.mode=subscribe&hub.verify_token=XXX&hub.challenge=123
    // Echo the challenge as plain text when the verify token matches webhook_secret.
    public function verify($query) {
        $mode      = $query['hub_mode']         ?? ($query['hub.mode']         ?? null);
        $token     = $query['hub_verify_token'] ?? ($query['hub.verify_token'] ?? null);
        $challenge = $query['hub_challenge']    ?? ($query['hub.challenge']    ?? null);

        // Note: PHP converts dots in query keys to underscores, so hub_* is what we usually receive.
        if ($mode === 'subscribe' && $token !== null && $this->verifyTokenMatches($token)) {
            header('Content-Type: text/plain');
            http_response_code(200);
            echo $challenge;
            return;
        }
        http_response_code(403);
        echo 'Verification failed';
    }

    private function verifyTokenMatches($token) {
        $stmt = $this->db->query("SELECT webhook_secret FROM whatsapp_settings WHERE webhook_secret IS NOT NULL AND webhook_secret <> ''");
        foreach ($stmt->fetchAll(PDO::FETCH_COLUMN) as $secret) {
            if (hash_equals((string)$secret, (string)$token)) return true;
        }
        return false;
    }

    /* ---------- POST: inbound messages + status callbacks ---------- */
    public function handleInbound($rawBody, $query, $headers) {
        // Twilio sends form-encoded; Meta sends JSON.
        $json = json_decode($rawBody, true);

        if (is_array($json) && isset($json['object'])) {
            $this->handleMeta($json);
        } elseif (!empty($_POST['From']) || !empty($_POST['Body']) || !empty($_POST['MessageSid'])) {
            $this->handleTwilio($_POST);
        } else {
            // Unknown shape — acknowledge so the provider doesn't retry forever.
            json_response(['success' => true, 'ignored' => true]);
            return;
        }
        // Always 200 quickly so providers don't disable the webhook.
        json_response(['success' => true]);
    }

    /* ===================== META CLOUD ===================== */
    private function handleMeta($payload) {
        foreach (($payload['entry'] ?? []) as $entry) {
            foreach (($entry['changes'] ?? []) as $change) {
                $value = $change['value'] ?? [];
                $phoneNumberId = $value['metadata']['phone_number_id'] ?? null;
                $propertyId = $this->resolvePropertyByPhoneNumberId($phoneNumberId);

                // Delivery/read/failed status callbacks
                foreach (($value['statuses'] ?? []) as $st) {
                    $this->updateDeliveryStatus($st['id'] ?? null, $st['status'] ?? null, $st['errors'][0]['title'] ?? null);
                }

                // Inbound messages
                $contacts = $value['contacts'][0] ?? [];
                $profileName = $contacts['profile']['name'] ?? null;
                foreach (($value['messages'] ?? []) as $msg) {
                    $from = $msg['from'] ?? null; // phone, digits only
                    $text = $msg['text']['body']
                        ?? ($msg['button']['text'] ?? ($msg['interactive']['list_reply']['title'] ?? null));
                    if ($from && $text !== null) {
                        $this->processInbound($propertyId, $from, $profileName, $text, $msg['id'] ?? null);
                    }
                }
            }
        }
    }

    private function resolvePropertyByPhoneNumberId($phoneNumberId) {
        if ($phoneNumberId) {
            $stmt = $this->db->prepare("SELECT property_id FROM whatsapp_settings WHERE phone_number_id = ? LIMIT 1");
            $stmt->execute([$phoneNumberId]);
            $pid = $stmt->fetchColumn();
            if ($pid) return (int)$pid;
        }
        return (int)($_GET['property_id'] ?? 1);
    }

    /* ===================== TWILIO ===================== */
    private function handleTwilio($post) {
        // Status callback?
        if (!empty($post['MessageStatus']) && !empty($post['MessageSid'])) {
            $this->updateDeliveryStatus($post['MessageSid'], strtolower($post['MessageStatus']), $post['ErrorMessage'] ?? null);
        }
        if (!empty($post['Body']) && !empty($post['From'])) {
            $from = preg_replace('/[^0-9]/', '', $post['From']); // strip "whatsapp:+"
            $propertyId = (int)($_GET['property_id'] ?? 1);
            $this->processInbound($propertyId, $from, $post['ProfileName'] ?? null, $post['Body'], $post['MessageSid'] ?? null);
        }
    }

    /* ===================== SHARED INBOUND PIPELINE ===================== */
    private function processInbound($propertyId, $phone, $name, $text, $providerMsgId) {
        // 1) Find the active guest session for this phone (last 10 digits match).
        $session = $this->findSessionByPhone($phone);
        if ($session && empty($propertyId)) $propertyId = $session['property_id'];

        // 2) Log the inbound message.
        $this->logInbound($phone, $name ?: ($session['guest_name'] ?? null), $text, $providerMsgId, $session['booking_id'] ?? null);

        // 3) Route through the AI concierge (reuses existing intent engine).
        $ai = new AIConciergeController($this->db, $this->config);
        $result = $ai->handleQuery([
            'message'       => $text,
            'session_token' => $session['session_token'] ?? null,
            'phone'         => $phone,
        ]);
        $reply = $result['reply'] ?? "Thanks! Our team has received your message.";

        // 4) Send the reply back out (logs as outbound + sends via active provider).
        $wa = new WhatsAppService($this->db, $this->config, $propertyId);
        $wa->sendMessage(
            $phone,
            $name ?: ($session['guest_name'] ?? 'Guest'),
            $reply,
            'custom',
            ['booking_id' => $session['booking_id'] ?? null]
        );
    }

    private function findSessionByPhone($phone) {
        $last10 = substr(preg_replace('/[^0-9]/', '', $phone), -10);
        if ($last10 === '') return null;
        $stmt = $this->db->prepare("
            SELECT * FROM whatsapp_sessions
            WHERE status='active' AND REPLACE(REPLACE(phone,'+',''),' ','') LIKE ?
            ORDER BY created_at DESC LIMIT 1
        ");
        $stmt->execute(['%' . $last10]);
        return $stmt->fetch(PDO::FETCH_ASSOC) ?: null;
    }

    private function logInbound($phone, $name, $body, $providerMsgId, $bookingId) {
        $stmt = $this->db->prepare("
            INSERT INTO whatsapp_messages
                (direction, recipient_phone, recipient_name, message_type, message_body,
                 related_booking_id, provider_message_id, status, sent_at)
            VALUES ('inbound', ?, ?, 'custom', ?, ?, ?, 'delivered', NOW())
        ");
        $stmt->execute([$phone, $name, $body, $bookingId, $providerMsgId]);
    }

    private function updateDeliveryStatus($providerMsgId, $status, $error = null) {
        if (!$providerMsgId || !$status) return;
        $allowed = ['sent','delivered','read','failed'];
        if (!in_array($status, $allowed, true)) {
            if ($status === 'undelivered') $status = 'failed';
            else return;
        }
        $stmt = $this->db->prepare("
            UPDATE whatsapp_messages
            SET status = ?, error_message = COALESCE(?, error_message)
            WHERE provider_message_id = ?
        ");
        $stmt->execute([$status, $error, $providerMsgId]);
    }
}
