<?php
date_default_timezone_set('America/Chihuahua');

/*
 * ============================================================
 * CONFIGURACIÓN
 * ============================================================
 */

$VERIFY_TOKEN = 'FiberX2024';

$N8N_WEBHOOK =
    'https://fiberx.app.n8n.cloud/webhook/whatsapp-6142528820';

$LOG_FILE =
    '/var/www/webhook/webhook_log.txt';

$MAX_ATTEMPTS =
    2;

/*
 * ============================================================
 * FUNCIONES
 * ============================================================
 */

function writeLog(
    string $logFile,
    string $message
): void {
    $timestamp =
        date('Y-m-d H:i:s P');

    file_put_contents(
        $logFile,
        sprintf(
            "[%s] %s%s",
            $timestamp,
            $message,
            PHP_EOL
        ),
        FILE_APPEND | LOCK_EX
    );
}

function extractEventIdentifiers(
    ?array $input
): array {
    $messageId = '';
    $customerId = '';
    $eventType = '';
    $messageText = '';

    $value =
        $input['entry'][0]['changes'][0]['value']
        ?? null;

    if (is_array($value)) {
        $message =
            $value['messages'][0]
            ?? null;

        if (is_array($message)) {
            $messageId =
                (string) (
                    $message['id']
                    ?? ''
                );

            $customerId =
                (string) (
                    $message['from']
                    ?? ''
                );

            $eventType =
                (string) (
                    $message['type']
                    ?? 'message'
                );

            $messageText =
                (string) (
                    $message['text']['body']
                    ?? ''
                );
        }

        $status =
            $value['statuses'][0]
            ?? null;

        if (
            $messageId === '' &&
            is_array($status)
        ) {
            $messageId =
                (string) (
                    $status['id']
                    ?? ''
                );

            $customerId =
                (string) (
                    $status['recipient_id']
                    ?? ''
                );

            $eventType =
                'status:' .
                (string) (
                    $status['status']
                    ?? 'unknown'
                );
        }
    }

    $messaging =
        $input['entry'][0]['messaging'][0]
        ?? null;

    if (
        $messageId === '' &&
        is_array($messaging)
    ) {
        $messageId =
            (string) (
                $messaging['message']['mid']
                ?? ''
            );

        $customerId =
            (string) (
                $messaging['sender']['id']
                ?? ''
            );

        $eventType =
            'messenger';

        $messageText =
            (string) (
                $messaging['message']['text']
                ?? ''
            );
    }

    return [
        'message_id' =>
            $messageId,

        'customer_id' =>
            $customerId,

        'event_type' =>
            $eventType,

        'message_text' =>
            $messageText
    ];
}

/*
 * ============================================================
 * VERIFICACIÓN DE META
 * ============================================================
 */

if (
    $_SERVER['REQUEST_METHOD'] ===
    'GET'
) {
    if (
        isset($_GET['hub_mode']) &&
        $_GET['hub_mode'] ===
        'subscribe' &&
        isset($_GET['hub_verify_token']) &&
        hash_equals(
            $VERIFY_TOKEN,
            (string) $_GET['hub_verify_token']
        )
    ) {
        header(
            'Content-Type: text/plain'
        );

        echo (string) (
            $_GET['hub_challenge']
            ?? ''
        );

        exit;
    }

    http_response_code(403);

    header(
        'Content-Type: text/plain'
    );

    echo 'Forbidden';

    exit;
}

/*
 * ============================================================
 * ACEPTAR SOLAMENTE POST
 * ============================================================
 */

if (
    $_SERVER['REQUEST_METHOD'] !==
    'POST'
) {
    http_response_code(405);

    header(
        'Allow: GET, POST'
    );

    header(
        'Content-Type: text/plain'
    );

    echo 'Method Not Allowed';

    exit;
}

/*
 * ============================================================
 * LEER Y VALIDAR EL PAYLOAD
 * ============================================================
 */

$rawInput =
    file_get_contents(
        'php://input'
    );

if (
    $rawInput === false ||
    trim($rawInput) === ''
) {
    writeLog(
        $LOG_FILE,
        '[INVALID PAYLOAD] Cuerpo vacío.'
    );

    http_response_code(400);

    header(
        'Content-Type: text/plain'
    );

    echo 'Empty payload';

    exit;
}

$input =
    json_decode(
        $rawInput,
        true
    );

if (
    !is_array($input) ||
    json_last_error() !==
    JSON_ERROR_NONE
) {
    writeLog(
        $LOG_FILE,
        sprintf(
            '[INVALID JSON] %s',
            json_last_error_msg()
        )
    );

    http_response_code(400);

    header(
        'Content-Type: text/plain'
    );

    echo 'Invalid JSON';

    exit;
}

$payloadHash =
    hash(
        'sha256',
        $rawInput
    );

$identifiers =
    extractEventIdentifiers(
        $input
    );

/*
 * Conserva el payload completo como hasta ahora.
 */

file_put_contents(
    $LOG_FILE,
    print_r(
        $input,
        true
    ) . PHP_EOL,
    FILE_APPEND | LOCK_EX
);

writeLog(
    $LOG_FILE,
    sprintf(
        '[RECEIVED] hash=%s | event=%s | customer=%s | message_id=%s | text=%s',
        $payloadHash,
        $identifiers['event_type'],
        $identifiers['customer_id'],
        $identifiers['message_id'],
        preg_replace(
            '/\s+/u',
            ' ',
            $identifiers['message_text']
        )
    )
);

/*
 * ============================================================
 * REENVIAR A N8N CON REINTENTO
 * ============================================================
 */

$forwarded =
    false;

$lastStatus =
    0;

$lastError =
    '';

$lastResponse =
    '';

for (
    $attempt = 1;
    $attempt <= $MAX_ATTEMPTS;
    $attempt += 1
) {
    $curl =
        curl_init(
            $N8N_WEBHOOK
        );

    curl_setopt_array(
        $curl,
        [
            CURLOPT_POST =>
                true,

            CURLOPT_POSTFIELDS =>
                $rawInput,

            CURLOPT_HTTPHEADER => [
                'Content-Type: application/json',
                'X-FiberX-Payload-Hash: ' .
                    $payloadHash,
                'X-FiberX-Forward-Attempt: ' .
                    $attempt
            ],

            CURLOPT_RETURNTRANSFER =>
                true,

            CURLOPT_CONNECTTIMEOUT =>
                4,

            CURLOPT_TIMEOUT =>
                8
        ]
    );

    $n8nResponse =
        curl_exec(
            $curl
        );

    $n8nError =
        curl_error(
            $curl
        );

    $n8nStatus =
        (int) curl_getinfo(
            $curl,
            CURLINFO_HTTP_CODE
        );

    curl_close(
        $curl
    );

    $lastStatus =
        $n8nStatus;

    $lastError =
        $n8nError;

    $lastResponse =
        $n8nResponse === false
            ? ''
            : (string) $n8nResponse;

    if (
        $n8nError === '' &&
        $n8nStatus >= 200 &&
        $n8nStatus < 300
    ) {
        $forwarded =
            true;

        writeLog(
            $LOG_FILE,
            sprintf(
                '[N8N OK] hash=%s | attempt=%d | HTTP %d | message_id=%s',
                $payloadHash,
                $attempt,
                $n8nStatus,
                $identifiers['message_id']
            )
        );

        break;
    }

    writeLog(
        $LOG_FILE,
        sprintf(
            '[N8N ERROR] hash=%s | attempt=%d | HTTP %d | error=%s | response=%s | message_id=%s',
            $payloadHash,
            $attempt,
            $n8nStatus,
            $n8nError,
            substr(
                $lastResponse,
                0,
                1000
            ),
            $identifiers['message_id']
        )
    );

    if (
        $attempt <
        $MAX_ATTEMPTS
    ) {
        usleep(
            750000
        );
    }
}

/*
 * ============================================================
 * RESPUESTA A META
 * ============================================================
 */

header(
    'Content-Type: text/plain'
);

if ($forwarded) {
    http_response_code(200);

    echo 'OK';

    exit;
}

/*
 * No confirmar falsamente la entrega.
 * Meta recibirá un error temporal y podrá reintentar.
 */

writeLog(
    $LOG_FILE,
    sprintf(
        '[DELIVERY FAILED] hash=%s | HTTP %d | error=%s | message_id=%s',
        $payloadHash,
        $lastStatus,
        $lastError,
        $identifiers['message_id']
    )
);

http_response_code(503);

header(
    'Retry-After: 30'
);

echo 'Temporary forwarding failure';