<?php
declare(strict_types=1);

require_once __DIR__ . '/config.php';

header('Content-Type: application/json');

try {
    $mysqli = db();
} catch (Throwable $e) {
    http_response_code(500);
    echo json_encode([
        'ok' => false,
        'error' => 'Database connection not available.'
    ]);
    exit;
}

if (!defined('LALAMOVE_PUSH_TOKEN') || trim((string)LALAMOVE_PUSH_TOKEN) === '') {
    http_response_code(500);
    echo json_encode([
        'ok' => false,
        'error' => 'LALAMOVE push token not configured.'
    ]);
    exit;
}

function rl_get_request_headers_lower(): array
{
    $headers = [];

    if (function_exists('getallheaders')) {
        $raw = getallheaders();
        if (is_array($raw)) {
            foreach ($raw as $key => $value) {
                $headers[strtolower((string)$key)] = (string)$value;
            }
        }
    }

    foreach ($_SERVER as $key => $value) {
        if (strpos($key, 'HTTP_') === 0) {
            $name = strtolower(str_replace('_', '-', substr($key, 5)));
            if (!isset($headers[$name])) {
                $headers[$name] = (string)$value;
            }
        }
    }

    return $headers;
}

function rl_normalize_phone(string $phone): string
{
    $phone = trim($phone);
    $digits = preg_replace('/\D+/', '', $phone);
    if ($digits === null) {
        return '';
    }

    if (strpos($digits, '0') === 0 && strlen($digits) >= 9) {
        return '6' . $digits;
    }

    return $digits;
}

function rl_log_webhook(mysqli $mysqli, string $source, string $eventType, ?string $externalId, string $headersJson, string $payload, string $status, ?string $errorMessage = null): void
{
    $sql = "INSERT INTO webhook_logs
            (source, event_type, external_id, headers, payload, process_status, error_message, received_at, processed_at)
            VALUES (?, ?, ?, ?, ?, ?, ?, NOW(), NOW())";

    $stmt = $mysqli->prepare($sql);
    if ($stmt) {
        $stmt->bind_param(
            'sssssss',
            $source,
            $eventType,
            $externalId,
            $headersJson,
            $payload,
            $status,
            $errorMessage
        );
        $stmt->execute();
        $stmt->close();
    }
}

function rl_to_mysql_datetime(string $raw): ?string
{
    $raw = trim($raw);
    if ($raw === '') {
        return null;
    }

    $ts = strtotime($raw);
    if ($ts === false) {
        return null;
    }

    return date('Y-m-d H:i:s', $ts);
}

function rl_is_assoc(array $arr): bool
{
    if ($arr === []) {
        return true;
    }

    return array_keys($arr) !== range(0, count($arr) - 1);
}

function rl_extract_orders_payload(array $data): array
{
    if (isset($data['orders']) && is_array($data['orders'])) {
        return array_values(array_filter($data['orders'], 'is_array'));
    }

    if (isset($data['order_id'])) {
        return [$data];
    }

    if (!rl_is_assoc($data)) {
        return array_values(array_filter($data, 'is_array'));
    }

    return [];
}

function rl_determine_scrape_status(string $status, string $overallStatusText, string $deliveryStatus): string
{
    $candidates = [
        strtolower(trim($status)),
        strtolower(trim($overallStatusText)),
        strtolower(trim($deliveryStatus)),
    ];

    foreach ($candidates as $value) {
        if ($value === 'completed') {
            return 'completed';
        }

        if ($value === 'cancelled' || $value === 'canceled') {
            return 'cancelled';
        }
    }

    return 'active';
}

function rl_pick_status_value(string $status, string $overallStatusText): string
{
    if ($status !== '') {
        return $status;
    }

    if ($overallStatusText !== '') {
        return $overallStatusText;
    }

    return 'active';
}

function rl_upsert_order(mysqli $mysqli, array $order, ?string $topLevelCheckedAt = null): array
{
    $orderId          = isset($order['order_id']) ? trim((string)$order['order_id']) : '';
    $recipientName    = isset($order['recipient_name']) ? trim((string)$order['recipient_name']) : '';
    $recipientPhone   = isset($order['recipient_phone']) ? trim((string)$order['recipient_phone']) : '';
    $recipientPhoneN  = rl_normalize_phone($recipientPhone);
    $destinationShort = isset($order['destination_short']) ? trim((string)$order['destination_short']) : '';
    $destinationFull  = isset($order['destination_full']) ? trim((string)$order['destination_full']) : '';
    $shareLink        = isset($order['share_link']) ? trim((string)$order['share_link']) : '';

    $status           = isset($order['status']) ? trim((string)$order['status']) : '';
    $overallStatus    = isset($order['overall_status_text']) ? trim((string)$order['overall_status_text']) : '';
    $deliveryStatus   = isset($order['delivery_status']) ? trim((string)$order['delivery_status']) : '';
    $pickedUpFound    = !empty($order['picked_up_found']) ? 1 : 0;
    $pickedUpAtText   = isset($order['picked_up_at_text']) ? trim((string)$order['picked_up_at_text']) : '';
    $driverName       = isset($order['driver_name']) ? trim((string)$order['driver_name']) : '';
    $vehiclePlate     = isset($order['vehicle_plate']) ? trim((string)$order['vehicle_plate']) : '';
    $vehicleType      = isset($order['vehicle_type']) ? trim((string)$order['vehicle_type']) : '';

    $checkedAtRaw = isset($order['checked_at']) ? trim((string)$order['checked_at']) : '';
    if ($checkedAtRaw === '' && $topLevelCheckedAt !== null) {
        $checkedAtRaw = trim($topLevelCheckedAt);
    }

    if ($orderId === '') {
        return [
            'ok' => false,
            'order_id' => '',
            'error' => 'Missing order_id.'
        ];
    }

    $checkedAtMysql = rl_to_mysql_datetime($checkedAtRaw);
    $scrapeStatus = rl_determine_scrape_status($status, $overallStatus, $deliveryStatus);
    $statusValue = rl_pick_status_value($status, $overallStatus);
    $shareFound = ($shareLink !== '') ? 1 : 0;
    $sourceUrl = 'office_pc_push';

    $rawJson = json_encode($order, JSON_UNESCAPED_SLASHES);
    if ($rawJson === false) {
        $rawJson = '{}';
    }

    $sql = "INSERT INTO lalamove_orders
            (
                lalamove_order_id,
                status,
                recipient_name,
                recipient_phone_raw,
                recipient_phone_normalized,
                destination_short,
                destination_full,
                overall_status_text,
                delivery_status,
                picked_up_found,
                picked_up_at_text,
                driver_name,
                vehicle_plate,
                vehicle_type,
                share_link,
                share_link_found,
                order_datetime,
                raw_json,
                scrape_status,
                source_url,
                first_seen_at,
                last_seen_at,
                created_at,
                updated_at
            )
            VALUES
            (
                ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW(), NOW(), NOW(), NOW()
            )
            ON DUPLICATE KEY UPDATE
                status = VALUES(status),
                recipient_name = VALUES(recipient_name),
                recipient_phone_raw = VALUES(recipient_phone_raw),
                recipient_phone_normalized = VALUES(recipient_phone_normalized),
                destination_short = VALUES(destination_short),
                destination_full = VALUES(destination_full),
                overall_status_text = CASE
                    WHEN VALUES(overall_status_text) <> '' THEN VALUES(overall_status_text)
                    ELSE overall_status_text
                END,
                delivery_status = CASE
                    WHEN VALUES(delivery_status) <> '' THEN VALUES(delivery_status)
                    ELSE delivery_status
                END,
                picked_up_found = CASE
                    WHEN VALUES(picked_up_found) = 1 THEN 1
                    ELSE picked_up_found
                END,
                picked_up_at_text = CASE
                    WHEN VALUES(picked_up_at_text) <> '' THEN VALUES(picked_up_at_text)
                    ELSE picked_up_at_text
                END,
                driver_name = CASE
                    WHEN VALUES(driver_name) <> '' THEN VALUES(driver_name)
                    ELSE driver_name
                END,
                vehicle_plate = CASE
                    WHEN VALUES(vehicle_plate) <> '' THEN VALUES(vehicle_plate)
                    ELSE vehicle_plate
                END,
                vehicle_type = CASE
                    WHEN VALUES(vehicle_type) <> '' THEN VALUES(vehicle_type)
                    ELSE vehicle_type
                END,
                share_link = CASE
                    WHEN VALUES(share_link) <> '' THEN VALUES(share_link)
                    ELSE share_link
                END,
                share_link_found = CASE
                    WHEN VALUES(share_link_found) = 1 THEN 1
                    ELSE share_link_found
                END,
                order_datetime = COALESCE(VALUES(order_datetime), order_datetime),
                raw_json = VALUES(raw_json),
                scrape_status = VALUES(scrape_status),
                source_url = VALUES(source_url),
                last_seen_at = NOW(),
                updated_at = NOW()";

    $stmt = $mysqli->prepare($sql);
    if (!$stmt) {
        return [
            'ok' => false,
            'order_id' => $orderId,
            'error' => 'Failed to prepare upsert statement: ' . $mysqli->error
        ];
    }

    $bindOk = $stmt->bind_param(
        'sssssssssisssssissss',
        $orderId,
        $statusValue,
        $recipientName,
        $recipientPhone,
        $recipientPhoneN,
        $destinationShort,
        $destinationFull,
        $overallStatus,
        $deliveryStatus,
        $pickedUpFound,
        $pickedUpAtText,
        $driverName,
        $vehiclePlate,
        $vehicleType,
        $shareLink,
        $shareFound,
        $checkedAtMysql,
        $rawJson,
        $scrapeStatus,
        $sourceUrl
    );

    if (!$bindOk) {
        $err = $stmt->error;
        $stmt->close();

        return [
            'ok' => false,
            'order_id' => $orderId,
            'error' => 'Failed to bind upsert statement: ' . $err
        ];
    }

    if (!$stmt->execute()) {
        $err = $stmt->error;
        $stmt->close();

        return [
            'ok' => false,
            'order_id' => $orderId,
            'error' => 'Failed to execute upsert: ' . $err
        ];
    }

    $insertId = (int)$stmt->insert_id;
    $affectedRows = (int)$stmt->affected_rows;
    $stmt->close();

    $rowId = 0;
    $lookupStmt = $mysqli->prepare("SELECT id FROM lalamove_orders WHERE lalamove_order_id = ? LIMIT 1");
    if ($lookupStmt) {
        $lookupStmt->bind_param('s', $orderId);
        $lookupStmt->execute();
        $lookupStmt->bind_result($rowId);
        $lookupStmt->fetch();
        $lookupStmt->close();
    }

    $action = ($insertId > 0 && $affectedRows === 1) ? 'inserted' : 'updated';

    return [
        'ok' => true,
        'order_id' => $orderId,
        'row_id' => (int)$rowId,
        'action' => $action,
        'delivery_status' => $deliveryStatus,
        'picked_up_found' => $pickedUpFound,
        'share_link_found' => $shareFound
    ];
}

try {
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        http_response_code(405);
        echo json_encode([
            'ok' => false,
            'error' => 'Method not allowed. Use POST.'
        ]);
        exit;
    }

    $headers = rl_get_request_headers_lower();
    $providedToken = isset($headers['x-lalamove-token']) ? trim($headers['x-lalamove-token']) : '';

    $rawPayload = file_get_contents('php://input');
    if ($rawPayload === false) {
        $rawPayload = '';
    }

    if (!hash_equals((string)LALAMOVE_PUSH_TOKEN, $providedToken)) {
        rl_log_webhook(
            $mysqli,
            'lalamove',
            'latest_orders_push',
            null,
            json_encode($headers),
            (string)$rawPayload,
            'failed',
            'Invalid X-Lalamove-Token'
        );

        http_response_code(401);
        echo json_encode([
            'ok' => false,
            'error' => 'Unauthorized.'
        ]);
        exit;
    }

    if (trim($rawPayload) === '') {
        http_response_code(400);
        echo json_encode([
            'ok' => false,
            'error' => 'Empty request body.'
        ]);
        exit;
    }

    $data = json_decode($rawPayload, true);
    if (!is_array($data)) {
        rl_log_webhook(
            $mysqli,
            'lalamove',
            'latest_orders_push',
            null,
            json_encode($headers),
            (string)$rawPayload,
            'failed',
            'Invalid JSON body'
        );

        http_response_code(400);
        echo json_encode([
            'ok' => false,
            'error' => 'Invalid JSON.'
        ]);
        exit;
    }

    $topLevelCheckedAt = isset($data['checked_at']) ? trim((string)$data['checked_at']) : null;
    $orders = rl_extract_orders_payload($data);

    if (count($orders) === 0) {
        rl_log_webhook(
            $mysqli,
            'lalamove',
            'latest_orders_push',
            null,
            json_encode($headers),
            (string)$rawPayload,
            'failed',
            'No orders found in payload'
        );

        http_response_code(400);
        echo json_encode([
            'ok' => false,
            'error' => 'No orders found in payload.'
        ]);
        exit;
    }

    $results = [];
    $processedCount = 0;
    $failedCount = 0;

    foreach ($orders as $order) {
        $result = rl_upsert_order($mysqli, $order, $topLevelCheckedAt);
        $results[] = $result;

        if (!empty($result['ok'])) {
            $processedCount++;
        } else {
            $failedCount++;
        }
    }

    rl_log_webhook(
        $mysqli,
        'lalamove',
        'latest_orders_push',
        null,
        json_encode($headers),
        (string)$rawPayload,
        ($failedCount === 0 ? 'processed' : 'partial'),
        ($failedCount === 0 ? null : 'Some orders failed to process')
    );

    echo json_encode([
        'ok' => ($processedCount > 0),
        'received_orders' => count($orders),
        'processed_orders' => $processedCount,
        'failed_orders' => $failedCount,
        'results' => $results
    ]);
    exit;

} catch (Throwable $e) {
    $headers = isset($headers) && is_array($headers) ? $headers : [];
    $rawPayload = isset($rawPayload) ? $rawPayload : '';

    rl_log_webhook(
        $mysqli,
        'lalamove',
        'latest_orders_push',
        null,
        json_encode($headers),
        (string)$rawPayload,
        'failed',
        $e->getMessage()
    );

    http_response_code(500);
    echo json_encode([
        'ok' => false,
        'error' => $e->getMessage()
    ]);
    exit;
}