<?php

require_once __DIR__ . '/agent_identity.php';

function wa_default_api_url(): string
{
    return 'https://api.fonnte.com/send';
}

function wa_bridge_default_base_url(): string
{
    return 'http://127.0.0.1:3001';
}

function wa_normalize_number(string $value): string
{
    $value = trim($value);
    $digits = preg_replace('/\D+/', '', $value);

    if ($digits === null || $digits === '') {
        return '';
    }

    // International prefix like 00380... becomes 380...
    if (strpos($digits, '00') === 0 && strlen($digits) > 2) {
        return substr($digits, 2);
    }

    if (strpos($digits, '62') === 0) {
        return $digits;
    }

    if (strpos($digits, '8') === 0) {
        return '62' . $digits;
    }

    // Indonesian mobile local format 08xxxx -> 628xxxx
    if (preg_match('/^08\d{7,15}$/', $digits) === 1) {
        return '62' . substr($digits, 1);
    }

    // For non-Indonesian numbers accidentally typed with a single trunk zero,
    // prefer removing that zero instead of forcing 62.
    if (strpos($digits, '0') === 0 && strlen($digits) > 1) {
        return ltrim($digits, '0');
    }

    return $digits;
}

function wa_prepare_target(string $value): string
{
    $value = trim($value);

    if ($value === '') {
        return '';
    }

    if (strpos($value, '@') !== false) {
        return $value;
    }

    return wa_normalize_number($value);
}

function wa_parse_targets(string $raw): array
{
    $targets = [];
    $parts = preg_split('/[\r\n,;]+/', $raw) ?: [];

    foreach ($parts as $part) {
        $normalized = wa_normalize_number($part);

        if ($normalized !== '') {
            $targets[] = $normalized;
        }
    }

    return array_values(array_unique($targets));
}

function wa_get_config(mysqli $db): array
{
    static $cache = null;

    if ($cache !== null) {
        return $cache;
    }

    $default = [
        'enabled' => false,
        'legacy_enabled' => false,
        'api_url' => wa_default_api_url(),
        'token' => '',
        'admin_targets' => [],
    ];

    $apiName = mysqli_real_escape_string($db, 'WHATSAPP');
    $query = mysqli_query($db, "SELECT * FROM tb_api_config WHERE api_name = '$apiName' LIMIT 1");

    if (!$query || mysqli_num_rows($query) === 0) {
        $cache = $default;
        return $cache;
    }

    $row = mysqli_fetch_assoc($query) ?: [];
    $apiUrl = trim((string)($row['api_url'] ?? ''));
    $token = trim((string)($row['secret_key'] ?? ''));

    $cache = [
        'enabled' => $token !== '',
        'legacy_enabled' => $token !== '',
        'api_url' => $apiUrl !== '' ? $apiUrl : wa_default_api_url(),
        'token' => $token,
        'admin_targets' => wa_parse_targets((string)($row['agent_code'] ?? '')),
    ];

    return $cache;
}

function wa_get_bridge_config(mysqli $db): array
{
    static $cache = null;

    if ($cache !== null) {
        return $cache;
    }

    $default = [
        'enabled' => false,
        'base_url' => wa_bridge_default_base_url(),
        'api_key' => '',
        'session_name' => '',
    ];

    $apiName = mysqli_real_escape_string($db, 'WHATSAPP_BRIDGE');
    $query = mysqli_query($db, "SELECT * FROM tb_api_config WHERE api_name = '$apiName' LIMIT 1");

    if (!$query || mysqli_num_rows($query) === 0) {
        $cache = $default;
        return $cache;
    }

    $row = mysqli_fetch_assoc($query) ?: [];
    $baseUrl = rtrim(trim((string)($row['api_url'] ?? '')), '/');
    $apiKey = trim((string)($row['secret_key'] ?? ''));
    $sessionName = trim((string)($row['agent_code'] ?? ''));

    $cache = [
        'enabled' => $baseUrl !== '' && $apiKey !== '',
        'base_url' => $baseUrl !== '' ? $baseUrl : wa_bridge_default_base_url(),
        'api_key' => $apiKey,
        'session_name' => $sessionName,
    ];

    return $cache;
}

function wa_get_bot_config(mysqli $db): array
{
    $default = [
        'webhook_secret' => '',
    ];

    $apiName = mysqli_real_escape_string($db, 'WHATSAPP_BOT');
    $query = mysqli_query($db, "SELECT * FROM tb_api_config WHERE api_name = '$apiName' LIMIT 1");

    if (!$query || mysqli_num_rows($query) === 0) {
        return $default;
    }

    $row = mysqli_fetch_assoc($query) ?: [];

    return [
        'webhook_secret' => trim((string)($row['agent_code'] ?? '')),
    ];
}

function wa_is_configured(mysqli $db): bool
{
    $config = wa_get_config($db);
    $bridgeConfig = wa_get_bridge_config($db);

    return $config['legacy_enabled'] === true || $bridgeConfig['enabled'] === true;
}

function wa_agent_contact_column_exists(mysqli $db, string $column): bool
{
    $column = mysqli_real_escape_string($db, trim($column));
    $query = mysqli_query($db, "SHOW COLUMNS FROM tb_agent_contact LIKE '$column'");

    return $query && mysqli_num_rows($query) > 0;
}

function wa_ensure_agent_contact_table(mysqli $db): bool
{
    static $ensured = null;

    if ($ensured === true) {
        return true;
    }

    $sql = "CREATE TABLE IF NOT EXISTS tb_agent_contact (
        agent_code varchar(255) NOT NULL,
        whatsapp varchar(32) NOT NULL,
        is_verified tinyint(1) NOT NULL DEFAULT 0,
        otp_code varchar(16) DEFAULT NULL,
        otp_requested_at datetime DEFAULT NULL,
        otp_expires_at datetime DEFAULT NULL,
        verified_at datetime DEFAULT NULL,
        created_at timestamp NULL DEFAULT current_timestamp(),
        updated_at timestamp NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
        PRIMARY KEY (agent_code),
        KEY idx_whatsapp (whatsapp)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci";

    if (mysqli_query($db, $sql) !== true) {
        $ensured = false;
        return false;
    }

    $columns = [
        'is_verified' => "ALTER TABLE tb_agent_contact ADD COLUMN is_verified tinyint(1) NOT NULL DEFAULT 0 AFTER whatsapp",
        'otp_code' => "ALTER TABLE tb_agent_contact ADD COLUMN otp_code varchar(16) DEFAULT NULL AFTER is_verified",
        'otp_requested_at' => "ALTER TABLE tb_agent_contact ADD COLUMN otp_requested_at datetime DEFAULT NULL AFTER otp_code",
        'otp_expires_at' => "ALTER TABLE tb_agent_contact ADD COLUMN otp_expires_at datetime DEFAULT NULL AFTER otp_requested_at",
        'verified_at' => "ALTER TABLE tb_agent_contact ADD COLUMN verified_at datetime DEFAULT NULL AFTER otp_expires_at",
    ];

    foreach ($columns as $column => $alterSql) {
        if (wa_agent_contact_column_exists($db, $column)) {
            continue;
        }

        if (mysqli_query($db, $alterSql) !== true) {
            $ensured = false;
            return false;
        }
    }

    $ensured = true;
    return true;
}

function wa_agent_contact_default(): array
{
    return [
        'agent_code' => '',
        'whatsapp' => '',
        'is_verified' => false,
        'otp_code' => '',
        'otp_requested_at' => '',
        'otp_expires_at' => '',
        'verified_at' => '',
    ];
}

function wa_get_agent_contact(mysqli $db, string $agentName): array
{
    $default = wa_agent_contact_default();

    if (!wa_ensure_agent_contact_table($db)) {
        return $default;
    }

    $agentName = trim($agentName);
    $rawAgentName = $agentName;

    if ($agentName === '') {
        return $default;
    }

    $agent = function_exists('agent_find_by_identifier') ? agent_find_by_identifier($db, $agentName) : null;
    $contactKeys = $agent !== null ? agent_identity_values($agent) : [$agentName];
    $contactKeysSql = function_exists('agent_sql_values')
        ? agent_sql_values($db, $contactKeys)
        : "'" . mysqli_real_escape_string($db, $agentName) . "'";

    $query = mysqli_query(
        $db,
        "SELECT agent_code, whatsapp, is_verified, otp_code, otp_requested_at, otp_expires_at, verified_at
         FROM tb_agent_contact
         WHERE agent_code IN ($contactKeysSql)
         LIMIT 1"
    );

    if (!$query || mysqli_num_rows($query) === 0) {
        return $default;
    }

    $row = mysqli_fetch_assoc($query) ?: [];

    return [
        'agent_code' => $agent !== null ? agent_public_name($agent) : trim((string)($row['agent_code'] ?? $rawAgentName)),
        'whatsapp' => wa_normalize_number((string)($row['whatsapp'] ?? '')),
        'is_verified' => !empty($row['is_verified']),
        'otp_code' => trim((string)($row['otp_code'] ?? '')),
        'otp_requested_at' => trim((string)($row['otp_requested_at'] ?? '')),
        'otp_expires_at' => trim((string)($row['otp_expires_at'] ?? '')),
        'verified_at' => trim((string)($row['verified_at'] ?? '')),
    ];
}

function wa_get_agent_whatsapp(mysqli $db, string $agentName): string
{
    $contact = wa_get_agent_contact($db, $agentName);
    return (string)($contact['whatsapp'] ?? '');
}

function wa_get_verified_agent_whatsapp(mysqli $db, string $agentCode): string
{
    $contact = wa_get_agent_contact($db, $agentCode);

    if (!empty($contact['is_verified'])) {
        return (string)($contact['whatsapp'] ?? '');
    }

    return '';
}

function wa_agent_requires_verification(mysqli $db, string $agentCode): bool
{
    $contact = wa_get_agent_contact($db, $agentCode);

    return (string)($contact['whatsapp'] ?? '') === '' || empty($contact['is_verified']);
}

function wa_save_agent_whatsapp(mysqli $db, string $agentCode, string $whatsapp): bool
{
    if (!wa_ensure_agent_contact_table($db)) {
        return false;
    }

    $agentCode = trim($agentCode);
    $whatsapp = wa_normalize_number($whatsapp);
    $currentContact = wa_get_agent_contact($db, $agentCode);

    if ($agentCode === '') {
        return false;
    }

    if ($whatsapp === '') {
        return mysqli_query($db, "DELETE FROM tb_agent_contact WHERE agent_code = '$agentCode'") === true;
    }

    if ((string)($currentContact['whatsapp'] ?? '') === $whatsapp) {
        return true;
    }

    $agentCodeEscaped = mysqli_real_escape_string($db, $agentCode);
    $whatsappEscaped = mysqli_real_escape_string($db, $whatsapp);

    return mysqli_query(
        $db,
        "INSERT INTO tb_agent_contact (
            agent_code,
            whatsapp,
            is_verified,
            otp_code,
            otp_requested_at,
            otp_expires_at,
            verified_at
        ) VALUES (
            '$agentCodeEscaped',
            '$whatsappEscaped',
            0,
            NULL,
            NULL,
            NULL,
            NULL
        )
         ON DUPLICATE KEY UPDATE
            whatsapp = VALUES(whatsapp),
            is_verified = 0,
            otp_code = NULL,
            otp_requested_at = NULL,
            otp_expires_at = NULL,
            verified_at = NULL"
    ) === true;
}

function wa_mask_number(string $whatsapp): string
{
    $whatsapp = wa_normalize_number($whatsapp);
    $length = strlen($whatsapp);

    if ($length <= 6) {
        return $whatsapp;
    }

    $prefixLength = min(4, max(2, $length - 4));
    $suffixLength = min(3, max(2, $length - $prefixLength));
    $maskLength = max(3, $length - $prefixLength - $suffixLength);

    return substr($whatsapp, 0, $prefixLength)
        . str_repeat('*', $maskLength)
        . substr($whatsapp, -$suffixLength);
}

function wa_request_agent_otp(mysqli $db, string $agentCode, string $whatsapp): array
{
    if (!wa_ensure_agent_contact_table($db)) {
        wa_log('agent_otp_failed', [
            'reason' => 'contact_storage_failed',
            'agent_code' => $agentCode,
        ]);
        return [
            'ok' => false,
            'code' => 'contact_storage_failed',
        ];
    }

    $agentCode = trim($agentCode);
    $whatsapp = wa_normalize_number($whatsapp);

    if ($agentCode === '') {
        wa_log('agent_otp_failed', [
            'reason' => 'agent_not_found',
        ]);
        return [
            'ok' => false,
            'code' => 'agent_not_found',
        ];
    }

    if ($whatsapp === '' || strlen($whatsapp) < 10 || strlen($whatsapp) > 16) {
        wa_log('agent_otp_failed', [
            'reason' => 'invalid_whatsapp',
            'agent_code' => $agentCode,
            'whatsapp' => $whatsapp,
        ]);
        return [
            'ok' => false,
            'code' => 'invalid_whatsapp',
        ];
    }

    $currentContact = wa_get_agent_contact($db, $agentCode);

    if ((string)($currentContact['whatsapp'] ?? '') === $whatsapp && (string)($currentContact['otp_requested_at'] ?? '') !== '') {
        $requestedAtTs = strtotime((string)$currentContact['otp_requested_at']);

        if ($requestedAtTs !== false && $requestedAtTs > (time() - 60)) {
            wa_log('agent_otp_failed', [
                'reason' => 'otp_throttled',
                'agent_code' => $agentCode,
                'whatsapp' => $whatsapp,
            ]);
            return [
                'ok' => false,
                'code' => 'otp_throttled',
            ];
        }
    }

    if (!wa_save_agent_whatsapp($db, $agentCode, $whatsapp)) {
        wa_log('agent_otp_failed', [
            'reason' => 'contact_storage_failed',
            'agent_code' => $agentCode,
            'whatsapp' => $whatsapp,
        ]);
        return [
            'ok' => false,
            'code' => 'contact_storage_failed',
        ];
    }

    $legacyConfig = wa_get_config($db);
    $bridgeConfig = wa_get_bridge_config($db);

    if (!$legacyConfig['legacy_enabled'] && !$bridgeConfig['enabled']) {
        wa_log('agent_otp_failed', [
            'reason' => 'whatsapp_not_configured',
            'agent_code' => $agentCode,
            'whatsapp' => $whatsapp,
        ]);
        return [
            'ok' => false,
            'code' => 'whatsapp_not_configured',
        ];
    }

    if ($legacyConfig['legacy_enabled'] && !$bridgeConfig['enabled']) {
        $fonnteStatus = wa_fonnte_status($db);
        $fonnteState = (string)($fonnteStatus['state'] ?? 'unknown');

        if ($fonnteState !== 'ready') {
            wa_log('agent_otp_failed', [
                'reason' => 'whatsapp_device_not_ready',
                'agent_code' => $agentCode,
                'whatsapp' => $whatsapp,
                'state' => $fonnteState,
                'message' => (string)($fonnteStatus['message'] ?? ''),
            ]);

            return [
                'ok' => false,
                'code' => 'whatsapp_device_not_ready',
                'state' => $fonnteState,
                'message' => (string)($fonnteStatus['message'] ?? ''),
            ];
        }
    }

    $agentCodeEscaped = mysqli_real_escape_string($db, $agentCode);
    $otpCode = str_pad((string)random_int(0, 999999), 6, '0', STR_PAD_LEFT);
    $otpCodeEscaped = mysqli_real_escape_string($db, $otpCode);
    $requestedAt = date('Y-m-d H:i:s');
    $expiresAt = date('Y-m-d H:i:s', time() + 300);
    $requestedAtEscaped = mysqli_real_escape_string($db, $requestedAt);
    $expiresAtEscaped = mysqli_real_escape_string($db, $expiresAt);

    if (!mysqli_query(
        $db,
        "UPDATE tb_agent_contact
         SET
            otp_code = '$otpCodeEscaped',
            otp_requested_at = '$requestedAtEscaped',
            otp_expires_at = '$expiresAtEscaped',
            is_verified = 0,
            verified_at = NULL
         WHERE agent_code = '$agentCodeEscaped'"
    )) {
        wa_log('agent_otp_failed', [
            'reason' => 'contact_storage_failed',
            'agent_code' => $agentCode,
            'whatsapp' => $whatsapp,
        ]);
        return [
            'ok' => false,
            'code' => 'contact_storage_failed',
        ];
    }

    $otpTimeLabel = date('d/m/Y H:i') . ' WIB';
    $message = "🔐 NOXPAY API - Verifikasi WhatsApp\n\n"
        . "Status: ✅ Kode OTP\n"
        . "Kode OTP: {$otpCode}\n"
        . "Berlaku: 5 menit\n"
        . "Waktu: {$otpTimeLabel}\n\n"
        . "⚠️ Jangan bagikan kode ini ke siapa pun.";

    if (!wa_send_message($db, $whatsapp, $message)) {
        mysqli_query(
            $db,
            "UPDATE tb_agent_contact
             SET
                otp_code = NULL,
                otp_requested_at = NULL,
                otp_expires_at = NULL
             WHERE agent_code = '$agentCodeEscaped'"
        );

        wa_log('agent_otp_failed', [
            'reason' => 'otp_send_failed',
            'agent_code' => $agentCode,
            'whatsapp' => $whatsapp,
        ]);

        return [
            'ok' => false,
            'code' => 'otp_send_failed',
        ];
    }

    wa_log('agent_otp_sent', [
        'agent_code' => $agentCode,
        'whatsapp' => $whatsapp,
        'expires_at' => $expiresAt,
    ]);

    return [
        'ok' => true,
        'code' => 'otp_sent',
        'whatsapp' => $whatsapp,
        'masked_whatsapp' => wa_mask_number($whatsapp),
        'expires_at' => $expiresAt,
    ];
}

function wa_verify_agent_otp(mysqli $db, string $agentCode, string $otpCode): array
{
    if (!wa_ensure_agent_contact_table($db)) {
        return [
            'ok' => false,
            'code' => 'contact_storage_failed',
        ];
    }

    $agentCode = trim($agentCode);
    $otpCode = trim($otpCode);

    if ($agentCode === '') {
        return [
            'ok' => false,
            'code' => 'agent_not_found',
        ];
    }

    if ($otpCode === '') {
        wa_log('agent_otp_verify_failed', [
            'reason' => 'otp_required',
            'agent_code' => $agentCode,
        ]);
        return [
            'ok' => false,
            'code' => 'otp_required',
        ];
    }

    $contact = wa_get_agent_contact($db, $agentCode);

    if ((string)($contact['whatsapp'] ?? '') === '') {
        wa_log('agent_otp_verify_failed', [
            'reason' => 'whatsapp_required',
            'agent_code' => $agentCode,
        ]);
        return [
            'ok' => false,
            'code' => 'whatsapp_required',
        ];
    }

    if ((string)($contact['otp_code'] ?? '') === '') {
        wa_log('agent_otp_verify_failed', [
            'reason' => 'otp_invalid',
            'agent_code' => $agentCode,
        ]);
        return [
            'ok' => false,
            'code' => 'otp_invalid',
        ];
    }

    $expiresAtTs = strtotime((string)($contact['otp_expires_at'] ?? ''));

    if ($expiresAtTs === false || $expiresAtTs < time()) {
        $agentCodeEscaped = mysqli_real_escape_string($db, $agentCode);
        mysqli_query(
            $db,
            "UPDATE tb_agent_contact
             SET
                otp_code = NULL,
                otp_requested_at = NULL,
                otp_expires_at = NULL
             WHERE agent_code = '$agentCodeEscaped'"
        );

        wa_log('agent_otp_verify_failed', [
            'reason' => 'otp_invalid',
            'agent_code' => $agentCode,
            'whatsapp' => (string)($contact['whatsapp'] ?? ''),
        ]);

        return [
            'ok' => false,
            'code' => 'otp_invalid',
        ];
    }

    if (!hash_equals((string)$contact['otp_code'], $otpCode)) {
        wa_log('agent_otp_verify_failed', [
            'reason' => 'otp_invalid',
            'agent_code' => $agentCode,
            'whatsapp' => (string)($contact['whatsapp'] ?? ''),
        ]);
        return [
            'ok' => false,
            'code' => 'otp_invalid',
        ];
    }

    $agentCodeEscaped = mysqli_real_escape_string($db, $agentCode);

    if (!mysqli_query(
        $db,
        "UPDATE tb_agent_contact
         SET
            is_verified = 1,
            verified_at = NOW(),
            otp_code = NULL,
            otp_requested_at = NULL,
            otp_expires_at = NULL
         WHERE agent_code = '$agentCodeEscaped'"
    )) {
        wa_log('agent_otp_verify_failed', [
            'reason' => 'contact_storage_failed',
            'agent_code' => $agentCode,
            'whatsapp' => (string)($contact['whatsapp'] ?? ''),
        ]);
        return [
            'ok' => false,
            'code' => 'contact_storage_failed',
        ];
    }

    wa_log('agent_otp_verified', [
        'agent_code' => $agentCode,
        'whatsapp' => (string)$contact['whatsapp'],
    ]);

    return [
        'ok' => true,
        'code' => 'otp_verified',
        'whatsapp' => (string)$contact['whatsapp'],
    ];
}

function wa_find_agent_by_whatsapp(mysqli $db, string $whatsapp): ?array
{
    if (!wa_ensure_agent_contact_table($db)) {
        return null;
    }

    $whatsapp = wa_normalize_number($whatsapp);

    if ($whatsapp === '') {
        return null;
    }

    $whatsappEscaped = mysqli_real_escape_string($db, $whatsapp);
    $query = mysqli_query(
        $db,
        "SELECT a.*, c.whatsapp
         FROM tb_agent_contact c
         INNER JOIN tb_agent a ON a.agent_name = c.agent_code OR a.agent_code = c.agent_code
         WHERE c.whatsapp = '$whatsappEscaped'
           AND c.is_verified = 1
         LIMIT 1"
    );

    if (!$query || mysqli_num_rows($query) === 0) {
        return null;
    }

    return mysqli_fetch_assoc($query) ?: null;
}

function wa_log(string $type, array $context = []): void
{
    $path = dirname(__DIR__) . DIRECTORY_SEPARATOR . 'logs' . DIRECTORY_SEPARATOR . 'whatsapp.log';
    $record = [
        'type' => $type,
        'time' => date('c'),
        'context' => $context,
    ];

    @file_put_contents(
        $path,
        json_encode($record, JSON_UNESCAPED_SLASHES) . PHP_EOL,
        FILE_APPEND | LOCK_EX
    );
}

function wa_http_request(
    string $method,
    string $url,
    array $headers = [],
    ?array $payload = null,
    int $connectTimeout = 10,
    int $timeout = 20
): array {
    $ch = curl_init($url);
    $options = [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CUSTOMREQUEST => strtoupper($method),
        CURLOPT_HTTPHEADER => $headers,
        CURLOPT_CONNECTTIMEOUT => $connectTimeout,
        CURLOPT_TIMEOUT => $timeout,
    ];

    if ($payload !== null) {
        $options[CURLOPT_POSTFIELDS] = json_encode($payload, JSON_UNESCAPED_SLASHES);
    }

    curl_setopt_array($ch, $options);

    $response = curl_exec($ch);
    $httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curlError = curl_error($ch);
    curl_close($ch);

    return [
        'body' => is_string($response) ? $response : '',
        'http_code' => $httpCode,
        'error' => $curlError,
        'json' => is_string($response) ? (json_decode($response, true) ?: []) : [],
    ];
}

function wa_fonnte_origin(string $apiUrl): string
{
    $parts = parse_url($apiUrl);

    if (!is_array($parts) || empty($parts['scheme']) || empty($parts['host'])) {
        return 'https://api.fonnte.com';
    }

    $origin = $parts['scheme'] . '://' . $parts['host'];

    if (!empty($parts['port'])) {
        $origin .= ':' . $parts['port'];
    }

    return $origin;
}

function wa_fonnte_request(mysqli $db, string $path, array $payload = []): array
{
    $config = wa_get_config($db);

    if ($config['token'] === '') {
        return [
            'ok' => false,
            'http_code' => 0,
            'body' => '',
            'json' => [],
            'error' => 'Fonnte token is not configured',
        ];
    }

    $url = wa_fonnte_origin($config['api_url']) . '/' . ltrim($path, '/');
    $ch = curl_init($url);

    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => $payload,
        CURLOPT_HTTPHEADER => [
            'Authorization: ' . $config['token'],
        ],
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_TIMEOUT => 20,
    ]);

    $response = curl_exec($ch);
    $httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curlError = curl_error($ch);
    curl_close($ch);

    return [
        'ok' => $curlError === '' && $httpCode >= 200 && $httpCode < 300,
        'http_code' => $httpCode,
        'body' => is_string($response) ? $response : '',
        'json' => is_string($response) ? (json_decode($response, true) ?: []) : [],
        'error' => $curlError,
    ];
}

function wa_fonnte_status(mysqli $db): array
{
    $config = wa_get_config($db);

    if ($config['token'] === '') {
        return [
            'configured' => false,
            'ok' => false,
            'state' => 'not_configured',
            'message' => 'Token Fonnte belum diisi.',
            'qr_data_url' => '',
            'device' => '',
            'name' => '',
            'quota' => '',
            'expired' => '',
        ];
    }

    $deviceResponse = wa_fonnte_request($db, 'device');
    $deviceJson = $deviceResponse['json'];

    if (!$deviceResponse['ok'] || !is_array($deviceJson) || empty($deviceJson['status'])) {
        return [
            'configured' => true,
            'ok' => false,
            'state' => 'error',
            'message' => (string)($deviceJson['reason'] ?? ($deviceResponse['error'] !== '' ? $deviceResponse['error'] : 'Gagal membaca device profile Fonnte.')),
            'qr_data_url' => '',
            'device' => '',
            'name' => '',
            'quota' => '',
            'expired' => '',
        ];
    }

    $deviceStatus = strtolower((string)($deviceJson['device_status'] ?? 'disconnect'));
    $result = [
        'configured' => true,
        'ok' => true,
        'state' => $deviceStatus === 'connect' ? 'ready' : 'disconnect',
        'message' => $deviceStatus === 'connect'
            ? 'Device Fonnte sudah terhubung.'
            : 'Device Fonnte belum terhubung. Scan QR untuk login.',
        'qr_data_url' => '',
        'device' => (string)($deviceJson['device'] ?? ''),
        'name' => (string)($deviceJson['name'] ?? ''),
        'quota' => (string)($deviceJson['quota'] ?? ''),
        'expired' => (string)($deviceJson['expired'] ?? ''),
        'device_status' => $deviceStatus,
    ];

    if ($deviceStatus === 'connect') {
        return $result;
    }

    $qrResponse = wa_fonnte_request($db, 'qr', [
        'type' => 'qr',
    ]);

    $qrJson = $qrResponse['json'];

    if ($qrResponse['ok'] && is_array($qrJson) && !empty($qrJson['status']) && !empty($qrJson['url'])) {
        $result['state'] = 'qr';
        $result['message'] = 'QR Fonnte siap dipindai.';
        $result['qr_data_url'] = 'data:image/png;base64,' . trim((string)$qrJson['url']);
        return $result;
    }

    $reason = is_array($qrJson) ? (string)($qrJson['reason'] ?? '') : '';
    if ($reason !== '') {
        $result['message'] = $reason;
    }

    return $result;
}

function wa_legacy_send_message(mysqli $db, string $target, string $message): bool
{
    $config = wa_get_config($db);
    $target = wa_prepare_target($target);

    if (!$config['legacy_enabled']) {
        wa_log('send_skipped', [
            'reason' => 'fonnte_not_configured',
            'target' => $target,
        ]);
        return false;
    }

    if ($target === '' || trim($message) === '') {
        wa_log('send_skipped', [
            'reason' => $target === '' ? 'invalid_target' : 'empty_message',
            'target' => $target,
        ]);
        return false;
    }

    $payload = [
        'target' => $target,
        'message' => $message,
        // Bypass Fonnte default country code (62) because targets are already normalized
        // into full international numbers such as 628..., 380..., 601..., etc.
        'countryCode' => '0',
    ];

    $ch = curl_init($config['api_url']);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => $payload,
        CURLOPT_HTTPHEADER => [
            'Authorization: ' . $config['token'],
        ],
        CURLOPT_CONNECTTIMEOUT => 10,
        CURLOPT_TIMEOUT => 20,
    ]);

    $response = curl_exec($ch);
    $httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $curlError = curl_error($ch);
    curl_close($ch);

    if ($curlError !== '') {
        wa_log('send_error', [
            'target' => $target,
            'error' => $curlError,
        ]);
        return false;
    }

    $decoded = json_decode((string)$response, true);
    $success = $httpCode >= 200
        && $httpCode < 300
        && is_array($decoded)
        && isset($decoded['status'])
        && !empty($decoded['status']);

    if (!$success) {
        wa_log('send_failed', [
            'target' => $target,
            'http_code' => $httpCode,
            'response' => is_string($response) ? substr($response, 0, 1000) : '',
        ]);
        return false;
    }

    wa_log('send_success', [
        'target' => $target,
        'http_code' => $httpCode,
    ]);

    return true;
}

function wa_bridge_headers(array $config): array
{
    return [
        'Accept: application/json',
        'Content-Type: application/json',
        'X-API-Key: ' . $config['api_key'],
        'Authorization: Bearer ' . $config['api_key'],
    ];
}

function wa_bridge_request(mysqli $db, string $method, string $path, ?array $payload = null): array
{
    $bridgeConfig = wa_get_bridge_config($db);

    if (!$bridgeConfig['enabled']) {
        return [
            'ok' => false,
            'http_code' => 0,
            'body' => '',
            'json' => [],
            'error' => 'Bridge not configured',
        ];
    }

    $url = $bridgeConfig['base_url'] . '/' . ltrim($path, '/');
    $response = wa_http_request($method, $url, wa_bridge_headers($bridgeConfig), $payload);

    if ($response['error'] !== '') {
        wa_log('bridge_request_error', [
            'url' => $url,
            'error' => $response['error'],
        ]);
    }

    return [
        'ok' => $response['error'] === '' && $response['http_code'] >= 200 && $response['http_code'] < 300,
        'http_code' => $response['http_code'],
        'body' => $response['body'],
        'json' => is_array($response['json']) ? $response['json'] : [],
        'error' => $response['error'],
    ];
}

function wa_bridge_status(mysqli $db): array
{
    $bridgeConfig = wa_get_bridge_config($db);

    if (!$bridgeConfig['enabled']) {
        return [
            'configured' => false,
            'ok' => false,
            'state' => 'not_configured',
            'message' => 'Bridge WhatsApp belum dikonfigurasi.',
            'qr_data_url' => '',
            'me' => '',
            'session_name' => $bridgeConfig['session_name'],
        ];
    }

    $response = wa_bridge_request($db, 'GET', '/status');
    $json = $response['json'];

    if (!$response['ok'] || !is_array($json)) {
        return [
            'configured' => true,
            'ok' => false,
            'state' => 'offline',
            'message' => $response['error'] !== '' ? $response['error'] : 'Bridge tidak merespons dengan benar.',
            'qr_data_url' => '',
            'me' => '',
            'session_name' => $bridgeConfig['session_name'],
        ];
    }

    $data = is_array($json['data'] ?? null) ? $json['data'] : $json;

    return [
        'configured' => true,
        'ok' => !empty($json['ok']) || !empty($json['status']),
        'state' => (string)($data['state'] ?? 'unknown'),
        'message' => (string)($data['message'] ?? ($json['message'] ?? '')),
        'qr_data_url' => (string)($data['qr_data_url'] ?? ''),
        'me' => (string)($data['me'] ?? ''),
        'session_name' => (string)($data['session_name'] ?? $bridgeConfig['session_name']),
        'last_event_at' => (string)($data['last_event_at'] ?? ''),
        'webhook_configured' => !empty($data['webhook_configured']),
    ];
}

function wa_bridge_send_message(mysqli $db, string $target, string $message): bool
{
    $target = wa_normalize_number($target);

    if ($target === '' || trim($message) === '') {
        return false;
    }

    $response = wa_bridge_request($db, 'POST', '/send', [
        'target' => $target,
        'message' => $message,
    ]);

    if (!$response['ok']) {
        wa_log('bridge_send_failed', [
            'target' => $target,
            'http_code' => $response['http_code'],
            'error' => $response['error'],
            'body' => substr((string)$response['body'], 0, 1000),
        ]);
        return false;
    }

    $json = $response['json'];
    $success = is_array($json) && (
        !empty($json['ok']) ||
        !empty($json['status']) ||
        !empty($json['success'])
    );

    if (!$success) {
        wa_log('bridge_send_rejected', [
            'target' => $target,
            'http_code' => $response['http_code'],
            'body' => substr((string)$response['body'], 0, 1000),
        ]);
        return false;
    }

    wa_log('bridge_send_success', [
        'target' => $target,
        'http_code' => $response['http_code'],
    ]);

    return true;
}

function wa_send_message(mysqli $db, string $target, string $message): bool
{
    if (wa_legacy_send_message($db, $target, $message)) {
        return true;
    }

    $bridgeConfig = wa_get_bridge_config($db);

    if ($bridgeConfig['enabled'] && wa_bridge_send_message($db, $target, $message)) {
        return true;
    }

    return false;
}

function wa_send_admin_alert(mysqli $db, string $message): bool
{
    $config = wa_get_config($db);
    $sent = false;

    if (empty($config['admin_targets'])) {
        wa_log('admin_alert_skipped', [
            'reason' => 'no_admin_targets',
        ]);
        return false;
    }

    foreach ($config['admin_targets'] as $target) {
        $sent = wa_send_message($db, $target, $message) || $sent;
    }

    return $sent;
}

function wa_app_base_url(): string
{
    $scheme = 'http';

    if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO'])) {
        $scheme = trim(explode(',', (string)$_SERVER['HTTP_X_FORWARDED_PROTO'])[0]);
    } elseif (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') {
        $scheme = 'https';
    } elseif ((int)($_SERVER['SERVER_PORT'] ?? 80) === 443) {
        $scheme = 'https';
    }

    $host = $_SERVER['HTTP_HOST'] ?? ($_SERVER['SERVER_NAME'] ?? 'localhost');
    $scriptName = str_replace('\\', '/', (string)($_SERVER['SCRIPT_NAME'] ?? ''));
    $basePath = str_replace('\\', '/', dirname($scriptName));

    if ($basePath === '/' || $basePath === '\\' || $basePath === '.') {
        $basePath = '';
    }

    if (substr($basePath, -9) === '/function') {
        $basePath = substr($basePath, 0, -9);
    }

    $basePath = rtrim((string)$basePath, '/');

    return $scheme . '://' . $host . $basePath;
}

function wa_app_url(string $relativePath = ''): string
{
    $baseUrl = wa_app_base_url();

    if ($relativePath === '') {
        return $baseUrl;
    }

    if ($relativePath[0] !== '/') {
        $relativePath = '/' . $relativePath;
    }

    return $baseUrl . $relativePath;
}

function wa_webhook_url(): string
{
    return wa_app_url('function/whatsapp_webhook.php');
}

function wa_verify_webhook_secret(mysqli $db, array $payload): bool
{
    $config = wa_get_bot_config($db);
    $expected = trim((string)($config['webhook_secret'] ?? ''));

    if ($expected === '') {
        return true;
    }

    $candidates = [
        $payload['secret'] ?? null,
        $payload['secretkey'] ?? null,
        $payload['secret_key'] ?? null,
        $payload['webhook_secret'] ?? null,
        $_SERVER['HTTP_X_WEBHOOK_SECRET'] ?? null,
    ];

    foreach ($candidates as $candidate) {
        if (is_string($candidate) && trim($candidate) === $expected) {
            return true;
        }
    }

    wa_log('webhook_secret_mismatch', [
        'payload_keys' => array_keys($payload),
    ]);

    return false;
}
