<?php
/**
 * consignment_helpers.php - Courier status category mapping and list utilities
 */

/** Steadfast official delivery_status values (API doc V1). */
function officialDeliveryStatusKeys() {
    return [
        'pending',
        'delivered_approval_pending',
        'partial_delivered_approval_pending',
        'cancelled_approval_pending',
        'unknown_approval_pending',
        'delivered',
        'partial_delivered',
        'cancelled',
        'hold',
        'in_review',
        'unknown',
    ];
}

/**
 * Count rows whose raw_status matches an official delivery_status key (normalized).
 *
 * @param array<int,array> $normalizedRows from normalizeConsignmentRow()
 * @return array<string,int>
 */
function buildOfficialDeliveryStatusCounts(array $normalizedRows) {
    $keys = officialDeliveryStatusKeys();
    $counts = array_fill_keys($keys, 0);
    foreach ($normalizedRows as $r) {
        $raw = isset($r['raw_status']) ? (string) $r['raw_status'] : '';
        $norm = strtolower(trim(preg_replace('/\s+/', ' ', $raw)));
        $norm = str_replace([' ', '-'], '_', $norm);
        if (isset($counts[$norm])) {
            $counts[$norm]++;
        }
    }
    return $counts;
}

/** @return string One of: pending, approval_pending, delivered, partly_delivered, cancelled, in_review, exceptional, pick_n_drop */
function mapCourierStatusToCategory($rawStatus) {
    $s = strtolower(trim((string) $rawStatus));
    $s = preg_replace('/\s+/', ' ', $s);
    $norm = str_replace([' ', '-'], '_', $s);

    if ($norm === '' || $norm === 'null' || $norm === '-') {
        return 'exceptional';
    }

    // Official Steadfast delivery_status (doc) — checked before heuristics
    $officialUi = [
        'pending' => 'pending',
        'delivered_approval_pending' => 'approval_pending',
        'partial_delivered_approval_pending' => 'approval_pending',
        'cancelled_approval_pending' => 'approval_pending',
        'unknown_approval_pending' => 'approval_pending',
        'delivered' => 'delivered',
        'partial_delivered' => 'partly_delivered',
        'cancelled' => 'cancelled',
        'hold' => 'exceptional',
        'in_review' => 'in_review',
        'unknown' => 'exceptional',
    ];
    if (isset($officialUi[$norm])) {
        return $officialUi[$norm];
    }

    // Pick-n-Drop
    if (strpos($norm, 'pick') !== false && (strpos($norm, 'drop') !== false || strpos($norm, 'n_drop') !== false)) {
        return 'pick_n_drop';
    }
    if (strpos($s, 'pick-n-drop') !== false || strpos($s, 'pick n drop') !== false) {
        return 'pick_n_drop';
    }

    // Delivered
    if (strpos($norm, 'delivered') !== false && strpos($norm, 'part') === false && strpos($norm, 'partial') === false) {
        return 'delivered';
    }

    // Partly delivered
    if (strpos($norm, 'partly') !== false || strpos($norm, 'partial') !== false || strpos($norm, 'part_deliver') !== false) {
        return 'partly_delivered';
    }

    // Cancelled
    if (strpos($norm, 'cancel') !== false) {
        return 'cancelled';
    }

    // Approval pending
    if (strpos($norm, 'approval') !== false && strpos($norm, 'pending') !== false) {
        return 'approval_pending';
    }
    if ($norm === 'approval_pending' || strpos($s, 'approval pending') !== false) {
        return 'approval_pending';
    }

    // In review
    if (strpos($norm, 'review') !== false || strpos($s, 'in review') !== false) {
        return 'in_review';
    }

    // Pending (broad in-transit / waiting)
    $pendingHints = ['pending', 'process', 'hub', 'pickup', 'picked', 'transit', 'out_for', 'out for', 'received', 'assigned', 'dispatched'];
    foreach ($pendingHints as $h) {
        if (strpos($norm, str_replace(' ', '_', $h)) !== false || strpos($s, $h) !== false) {
            return 'pending';
        }
    }

    // Exceptional (hold, exception, failed, unknown)
    $exHints = ['exception', 'hold', 'fail', 'problem', 'issue', 'return', 'lost', 'damage'];
    foreach ($exHints as $h) {
        if (strpos($norm, $h) !== false) {
            return 'exceptional';
        }
    }

    return 'exceptional';
}

/**
 * @param array $row Raw courier row
 * @return array Normalized row with category
 */
function normalizeConsignmentRow(array $row) {
    $rawStatus = '';
    if (isset($row['status'])) {
        $rawStatus = is_scalar($row['status']) ? (string) $row['status'] : json_encode($row['status']);
    } elseif (isset($row['delivery_status'])) {
        $rawStatus = is_scalar($row['delivery_status']) ? (string) $row['delivery_status'] : '';
    } elseif (isset($row['parcel_status'])) {
        $rawStatus = is_scalar($row['parcel_status']) ? (string) $row['parcel_status'] : '';
    }

    $category = mapCourierStatusToCategory($rawStatus);

    return [
        'invoice' => (string) ($row['invoice'] ?? ''),
        'tracking_code' => (string) ($row['tracking_code'] ?? ''),
        'consignment_id' => (string) ($row['consignment_id'] ?? $row['id'] ?? ''),
        'raw_status' => $rawStatus,
        'category' => $category,
        'recipient_name' => (string) ($row['recipient_name'] ?? $row['recipientName'] ?? ''),
        'cod_amount' => $row['cod_amount'] ?? $row['cod'] ?? null,
        'created_at' => $row['created_at'] ?? $row['date'] ?? $row['order_date'] ?? null,
    ];
}

/**
 * @param array<int,array> $normalizedRows
 * @return array<string,int>
 */
function buildConsignmentTotals(array $normalizedRows) {
    $totals = [
        'all' => count($normalizedRows),
        'pending' => 0,
        'approval_pending' => 0,
        'delivered' => 0,
        'partly_delivered' => 0,
        'cancelled' => 0,
        'in_review' => 0,
        'exceptional' => 0,
        'pick_n_drop' => 0,
    ];
    foreach ($normalizedRows as $r) {
        $c = $r['category'] ?? 'exceptional';
        if (isset($totals[$c])) {
            $totals[$c]++;
        } else {
            $totals['exceptional']++;
        }
    }
    return $totals;
}

/**
 * @param array<int,array> $rows
 * @param string|null $fromDate Y-m-d
 * @param string|null $toDate Y-m-d
 */
function filterConsignmentsByDateRange(array $rows, $fromDate, $toDate) {
    if (empty($fromDate) && empty($toDate)) {
        return $rows;
    }
    $fromTs = $fromDate ? strtotime($fromDate . ' 00:00:00') : null;
    $toTs = $toDate ? strtotime($toDate . ' 23:59:59') : null;
    $out = [];
    foreach ($rows as $r) {
        $d = $r['created_at'] ?? null;
        if (empty($d)) {
            $out[] = $r;
            continue;
        }
        $ts = strtotime((string) $d);
        if ($ts === false) {
            $out[] = $r;
            continue;
        }
        if ($fromTs !== null && $ts < $fromTs) {
            continue;
        }
        if ($toTs !== null && $ts > $toTs) {
            continue;
        }
        $out[] = $r;
    }
    return $out;
}

/**
 * @param array<int,array> $rows
 * @param string $bucket all|pending|approval_pending|...
 */
function filterConsignmentsByBucket(array $rows, $bucket) {
    if ($bucket === '' || $bucket === 'all') {
        return $rows;
    }
    $out = [];
    foreach ($rows as $r) {
        if (($r['category'] ?? '') === $bucket) {
            $out[] = $r;
        }
    }
    return $out;
}

/**
 * @param array<int,mixed> $arr
 * @return array<int,array>
 */
function paginateArray(array $arr, $page, $limit) {
    $page = max(1, (int) $page);
    $limit = max(1, min(100, (int) $limit));
    $total = count($arr);
    $offset = ($page - 1) * $limit;
    $slice = array_slice($arr, $offset, $limit);
    return [
        'data' => $slice,
        'pagination' => [
            'page' => $page,
            'limit' => $limit,
            'total' => $total,
            'total_pages' => $limit > 0 ? (int) ceil($total / $limit) : 0,
        ],
    ];
}

/** @return array<int,string> */
function consignmentBucketKeys() {
    return ['all', 'pending', 'approval_pending', 'delivered', 'partly_delivered', 'cancelled', 'in_review', 'exceptional', 'pick_n_drop'];
}

function validateConsignmentBucket($bucket) {
    $bucket = strtolower(trim((string) $bucket));
    return in_array($bucket, consignmentBucketKeys(), true) ? $bucket : 'all';
}
