<?php
declare(strict_types=1);

function positive_int_values(array $values): array
{
    $normalized = array();

    foreach ($values as $value) {
        $value = (int) $value;

        if ($value <= 0 || in_array($value, $normalized, true)) {
            continue;
        }

        $normalized[] = $value;
    }

    return $normalized;
}

function order_service_summary_map(mysqli $conn, array $orderIds): array
{
    $orderIds = positive_int_values($orderIds);

    if (empty($orderIds)) {
        return array();
    }

    $placeholders = implode(',', array_fill(0, count($orderIds), '?'));
    $types = str_repeat('i', count($orderIds));
    $stmt = db_prepare($conn, "
        SELECT
            od.order_id,
            COUNT(*) AS item_count,
            GROUP_CONCAT(od.service_name_snapshot ORDER BY od.detail_id SEPARATOR ', ') AS service_names
        FROM order_details od
        WHERE od.order_id IN ({$placeholders})
        GROUP BY od.order_id
    ");
    $stmt->bind_param($types, ...$orderIds);
    $stmt->execute();
    $result = $stmt->get_result();
    $map = array();

    while ($row = $result->fetch_assoc()) {
        $orderId = (int) ($row['order_id'] ?? 0);
        if ($orderId <= 0) {
            continue;
        }

        $map[$orderId] = array(
            'item_count' => (int) ($row['item_count'] ?? 0),
            'service_names' => (string) ($row['service_names'] ?? ''),
        );
    }

    $stmt->close();

    return $map;
}

function order_payment_summary_defaults(): array
{
    return array(
        'total_amount' => 0.0,
        'paid_amount' => 0.0,
        'cash_amount' => 0.0,
        'transfer_amount' => 0.0,
        'card_amount' => 0.0,
        'unknown_amount' => 0.0,
        'admin_deposit_count' => 0,
        'admin_deposit_amount' => 0.0,
        'admin_deposit_method' => '',
        'admin_deposit_date' => null,
        'branch_deposit_count' => 0,
        'branch_deposit_amount' => 0.0,
        'branch_deposit_method' => '',
        'branch_deposit_date' => null,
        'last_payment_at' => null,
    );
}

function order_payment_summary_map(mysqli $conn, array $orderIds): array
{
    $orderIds = positive_int_values($orderIds);

    if (empty($orderIds)) {
        return array();
    }

    $placeholders = implode(',', array_fill(0, count($orderIds), '?'));
    $types = str_repeat('i', count($orderIds));
    $stmt = db_prepare($conn, "
        SELECT
            p.order_id,
            COALESCE(SUM(p.amount), 0) AS total_amount,
            SUM(CASE WHEN COALESCE(p.note, '') NOT IN ('Deposit at booking', 'Admin deposit at booking', 'Branch deposit at booking') THEN p.amount ELSE 0 END) AS paid_amount,
            SUM(CASE WHEN COALESCE(p.note, '') NOT IN ('Deposit at booking', 'Admin deposit at booking', 'Branch deposit at booking') AND p.payment_method = 'cash' THEN p.amount ELSE 0 END) AS cash_amount,
            SUM(CASE WHEN COALESCE(p.note, '') NOT IN ('Deposit at booking', 'Admin deposit at booking', 'Branch deposit at booking') AND p.payment_method IN ('transfer', 'qr') THEN p.amount ELSE 0 END) AS transfer_amount,
            SUM(CASE WHEN COALESCE(p.note, '') NOT IN ('Deposit at booking', 'Admin deposit at booking', 'Branch deposit at booking') AND p.payment_method = 'card' THEN p.amount ELSE 0 END) AS card_amount,
            SUM(CASE WHEN COALESCE(p.note, '') NOT IN ('Deposit at booking', 'Admin deposit at booking', 'Branch deposit at booking') AND p.payment_method NOT IN ('cash', 'transfer', 'qr', 'card') THEN p.amount ELSE 0 END) AS unknown_amount,
            SUM(CASE WHEN p.note IN ('Deposit at booking', 'Admin deposit at booking') THEN 1 ELSE 0 END) AS admin_deposit_count,
            SUM(CASE WHEN p.note IN ('Deposit at booking', 'Admin deposit at booking') THEN p.amount ELSE 0 END) AS admin_deposit_amount,
            SUBSTRING_INDEX(
                GROUP_CONCAT(
                    CASE WHEN p.note IN ('Deposit at booking', 'Admin deposit at booking') THEN p.payment_method ELSE NULL END
                    ORDER BY p.payment_date ASC, p.payment_id ASC
                    SEPARATOR ','
                ),
                ',',
                1
            ) AS admin_deposit_method,
            MIN(CASE WHEN p.note IN ('Deposit at booking', 'Admin deposit at booking') THEN p.payment_date END) AS admin_deposit_date,
            SUM(CASE WHEN p.note = 'Branch deposit at booking' THEN 1 ELSE 0 END) AS branch_deposit_count,
            SUM(CASE WHEN p.note = 'Branch deposit at booking' THEN p.amount ELSE 0 END) AS branch_deposit_amount,
            SUBSTRING_INDEX(
                GROUP_CONCAT(
                    CASE WHEN p.note = 'Branch deposit at booking' THEN p.payment_method ELSE NULL END
                    ORDER BY p.payment_date ASC, p.payment_id ASC
                    SEPARATOR ','
                ),
                ',',
                1
            ) AS branch_deposit_method,
            MIN(CASE WHEN p.note = 'Branch deposit at booking' THEN p.payment_date END) AS branch_deposit_date,
            MAX(p.payment_date) AS last_payment_at
        FROM payments p
        WHERE p.order_id IN ({$placeholders})
        GROUP BY p.order_id
    ");
    $stmt->bind_param($types, ...$orderIds);
    $stmt->execute();
    $result = $stmt->get_result();
    $map = array();

    while ($row = $result->fetch_assoc()) {
        $orderId = (int) ($row['order_id'] ?? 0);
        if ($orderId <= 0) {
            continue;
        }

        $map[$orderId] = array(
            'total_amount' => (float) ($row['total_amount'] ?? 0),
            'paid_amount' => (float) ($row['paid_amount'] ?? 0),
            'cash_amount' => (float) ($row['cash_amount'] ?? 0),
            'transfer_amount' => (float) ($row['transfer_amount'] ?? 0),
            'card_amount' => (float) ($row['card_amount'] ?? 0),
            'unknown_amount' => (float) ($row['unknown_amount'] ?? 0),
            'admin_deposit_count' => (int) ($row['admin_deposit_count'] ?? 0),
            'admin_deposit_amount' => (float) ($row['admin_deposit_amount'] ?? 0),
            'admin_deposit_method' => (string) ($row['admin_deposit_method'] ?? ''),
            'admin_deposit_date' => $row['admin_deposit_date'] ?? null,
            'branch_deposit_count' => (int) ($row['branch_deposit_count'] ?? 0),
            'branch_deposit_amount' => (float) ($row['branch_deposit_amount'] ?? 0),
            'branch_deposit_method' => (string) ($row['branch_deposit_method'] ?? ''),
            'branch_deposit_date' => $row['branch_deposit_date'] ?? null,
            'last_payment_at' => $row['last_payment_at'] ?? null,
        );
    }

    $stmt->close();

    return $map;
}

function customer_car_summary_map(mysqli $conn, array $customerIds): array
{
    $customerIds = positive_int_values($customerIds);

    if (empty($customerIds)) {
        return array();
    }

    $placeholders = implode(',', array_fill(0, count($customerIds), '?'));
    $types = str_repeat('i', count($customerIds));
    $stmt = db_prepare($conn, "
        SELECT
            car.customer_id,
            COUNT(*) AS car_count,
            GROUP_CONCAT(car.plate_number ORDER BY car.updated_at DESC, car.car_id DESC SEPARATOR ', ') AS plate_numbers
        FROM cars car
        WHERE car.customer_id IN ({$placeholders})
        GROUP BY car.customer_id
    ");
    $stmt->bind_param($types, ...$customerIds);
    $stmt->execute();
    $result = $stmt->get_result();
    $map = array();

    while ($row = $result->fetch_assoc()) {
        $customerId = (int) ($row['customer_id'] ?? 0);
        if ($customerId <= 0) {
            continue;
        }

        $map[$customerId] = array(
            'car_count' => (int) ($row['car_count'] ?? 0),
            'plate_numbers' => (string) ($row['plate_numbers'] ?? ''),
        );
    }

    $stmt->close();

    return $map;
}

function order_paid_amount(mysqli $conn, int $orderId): float
{
    if ($orderId <= 0) {
        return 0.0;
    }

    $stmt = db_prepare($conn, "
        SELECT COALESCE(SUM(amount), 0) AS paid_amount
        FROM payments
        WHERE order_id = ?
    ");
    $stmt->bind_param('i', $orderId);
    $stmt->execute();
    $row = $stmt->get_result()->fetch_assoc();
    $stmt->close();

    return max(0.0, (float) ($row['paid_amount'] ?? 0));
}

function order_balance_amount(float $totalPrice, float $paidAmount): float
{
    return max(0.0, $totalPrice - $paidAmount);
}

function order_payment_status_from_amounts(float $totalPrice, float $paidAmount): string
{
    $totalPrice = max(0.0, $totalPrice);
    $paidAmount = max(0.0, $paidAmount);

    if ($paidAmount > 0.0 && ($totalPrice <= 0.0 || $paidAmount + 0.00001 >= $totalPrice)) {
        return 'paid';
    }

    if ($paidAmount > 0.0) {
        return 'partial';
    }

    return 'unpaid';
}

function quick_service_order_state_from_amounts(float $totalPrice, float $paidAmount): array
{
    $paymentStatus = order_payment_status_from_amounts($totalPrice, $paidAmount);

    return array(
        'payment_status' => $paymentStatus,
        'status' => $paymentStatus === 'paid' ? 'done' : 'waiting',
        'finished_at' => $paymentStatus === 'paid' ? date('Y-m-d H:i:s') : null,
        'balance_amount' => order_balance_amount($totalPrice, $paidAmount),
    );
}

function order_reverse_loyalty_earn_if_exists(mysqli $conn, int $orderId, array $order, string $reasonMessage): void
{
    if ($orderId <= 0) {
        return;
    }

    $loyaltyLookupStmt = db_prepare($conn, "
        SELECT transaction_id, customer_id, points
        FROM loyalty_transactions
        WHERE order_id = ?
            AND transaction_type = 'earn'
        LIMIT 1
    ");
    $loyaltyLookupStmt->bind_param('i', $orderId);
    $loyaltyLookupStmt->execute();
    $earnedTransaction = $loyaltyLookupStmt->get_result()->fetch_assoc();
    $loyaltyLookupStmt->close();

    if (!$earnedTransaction) {
        return;
    }

    $transactionId = (int) ($earnedTransaction['transaction_id'] ?? 0);
    $points = (int) ($earnedTransaction['points'] ?? 0);
    $customerId = (int) ($earnedTransaction['customer_id'] ?? 0);

    $deleteLoyaltyStmt = db_prepare($conn, "
        DELETE FROM loyalty_transactions
        WHERE transaction_id = ?
    ");
    $deleteLoyaltyStmt->bind_param('i', $transactionId);
    $deleteLoyaltyStmt->execute();
    $deleteLoyaltyStmt->close();

    $customerStmt = db_prepare($conn, "
        UPDATE customers
        SET loyalty_points = GREATEST(0, loyalty_points - ?)
        WHERE customer_id = ?
    ");
    $customerStmt->bind_param('ii', $points, $customerId);
    $customerStmt->execute();
    $customerStmt->close();

    audit_log($conn, array(
        'module_key' => 'loyalty',
        'action_key' => 'reverse',
        'entity_type' => 'loyalty_transaction',
        'entity_id' => $transactionId,
        'order_id' => $orderId,
        'customer_id' => $customerId,
        'car_id' => (int) ($order['car_id'] ?? 0),
        'branch_name' => (string) ($order['branch_name'] ?? ''),
        'bill_number' => (string) ($order['bill_number'] ?? ''),
        'message' => $reasonMessage,
        'before' => array(
            'transaction_type' => 'earn',
            'points' => $points,
            'status' => 'active',
        ),
        'after' => array(
            'status' => 'reversed',
        ),
    ));
}

function log_process_failure(string $operation, Throwable $exception, array $context = array()): void
{
    $payload = array(
        'operation' => $operation,
        'exception' => get_class($exception),
        'message' => trim($exception->getMessage()),
        'context' => array(),
    );

    foreach ($context as $key => $value) {
        if (is_array($value)) {
            $payload['context'][(string) $key] = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            continue;
        }

        if ($value === null || is_scalar($value)) {
            $payload['context'][(string) $key] = $value;
            continue;
        }

        $payload['context'][(string) $key] = get_debug_type($value);
    }

    $encoded = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    if (!is_string($encoded)) {
        error_log('CCW ' . $operation . ' failed: ' . $exception->getMessage());

        return;
    }

    error_log('CCW ' . $operation . ' failed: ' . $encoded);
}
