<?php
/**
 * Расчёт стоимости доставки BXB (последняя миля 5POST) для yomamaz.ru.
 *
 * Модель (полностью повторяет tariff.py):
 *   стоимость(EUR) = магистраль(EU -> Москва) + последняя миля(зона, вес)
 *   магистраль      = LINEHAUL_EUR_PER_KG * вес_кг            (3 EUR/кг)
 *   последняя миля  = матрица BXB[зона][весовой_брекет]       (EUR, из bxb_tariffs.json)
 *   стоимость(RUB)  = round( стоимость(EUR) * EUR_RUB )
 *
 * Курс EUR->RUB зафиксирован навсегда по просьбе владельца (ЦБ РФ, 23.06.2026).
 * Наценки нет.
 */

const EUR_RUB             = 84.5863;   // фикс. курс, ЦБ РФ 23.06.2026
const LINEHAUL_EUR_PER_KG = 3.0;       // магистраль EU -> Москва
const MAX_POINT_WEIGHT_KG = 15.0;      // ограничение пункта 5POST (постамат)
const MAX_COURIER_WEIGHT_KG = 31.0;    // ограничение курьера КСЕ

/** Найти файл данных рядом со скриптом или в родительской папке. */
function data_path(string $name): string {
    foreach ([__DIR__ . '/' . $name, __DIR__ . '/../' . $name] as $p) {
        if (is_file($p)) return $p;
    }
    return __DIR__ . '/' . $name; // вернём «ожидаемый», чтобы ошибка была понятной
}

/** Лениво загружаемая матрица тарифов. */
function tariffs(): array {
    static $T = null;
    if ($T === null) {
        $raw = file_get_contents(data_path('bxb_tariffs.json'));
        $j = json_decode($raw, true);
        $pickup = [];
        foreach ($j['pickup'] as $zone => $row) {
            $pickup[(int)$zone] = array_map('floatval', $row);
        }
        $T = [
            'weights' => array_map('floatval', $j['pickup_weights']), // верхние границы брекетов
            'pickup'  => $pickup,                                     // zone -> [цены EUR]
        ];
    }
    return $T;
}

/** Индекс весового брекета: первый порог >= weight. null если вес выше максимума. */
function bracket_index(float $weight_kg): ?int {
    $weights = tariffs()['weights'];
    foreach ($weights as $i => $w) {
        if ($weight_kg <= $w + 1e-9) return $i;
    }
    return null;
}

/** Последняя миля из матрицы BXB, EUR. null если зона/вес недопустимы. */
function last_mile_eur(float $weight_kg, int $zone): ?float {
    $pickup = tariffs()['pickup'];
    if (!array_key_exists($zone, $pickup)) return null;
    $bi = bracket_index($weight_kg);
    if ($bi === null) return null;
    return (float)$pickup[$zone][$bi];
}

/**
 * Полный расчёт. Возвращает массив:
 *   ['ok','rub','eur','linehaul_eur','lastmile_eur','weight_kg','zone','reason']
 * ok=false, если доставка в пункт невозможна (вес > 15 кг или неизвестная зона).
 */
function calc(float $weight_kg, int $zone): array {
    $weight_kg = max($weight_kg, 0.0);
    if ($weight_kg > MAX_POINT_WEIGHT_KG) {
        return ['ok' => false,
                'reason' => "вес {$weight_kg} кг > " . MAX_POINT_WEIGHT_KG . " кг (лимит пункта)",
                'weight_kg' => $weight_kg, 'zone' => $zone];
    }
    $lm = last_mile_eur($weight_kg, $zone);
    if ($lm === null) {
        return ['ok' => false, 'reason' => "нет тарифа для зоны {$zone}",
                'weight_kg' => $weight_kg, 'zone' => $zone];
    }
    $lh  = LINEHAUL_EUR_PER_KG * $weight_kg;
    $eur = $lh + $lm;
    $rub = (int)round($eur * EUR_RUB);
    return [
        'ok'           => true,
        'rub'          => $rub,
        'eur'          => round($eur, 2),
        'linehaul_eur' => round($lh, 2),
        'lastmile_eur' => round($lm, 2),
        'weight_kg'    => $weight_kg,
        'zone'         => $zone,
        'reason'       => null,
    ];
}

/* ===================== КУРЬЕР КСЕ (матрица courier) ===================== */

/** Лениво загружаемая матрица курьерских тарифов КСЕ. */
function courier_tariffs(): array {
    static $T = null;
    if ($T === null) {
        $raw = file_get_contents(data_path('bxb_tariffs.json'));
        $j = json_decode($raw, true);
        $courier = [];
        foreach ($j['courier'] as $zone => $row) {
            $courier[(int)$zone] = array_map('floatval', $row);
        }
        $T = [
            'weights' => array_map('floatval', $j['courier_weights']), // верхние границы брекетов
            'courier' => $courier,                                     // zone -> [цены EUR]
        ];
    }
    return $T;
}

/** Индекс весового брекета для курьера. null если вес выше максимума. */
function courier_bracket_index(float $weight_kg): ?int {
    $weights = courier_tariffs()['weights'];
    foreach ($weights as $i => $w) {
        if ($weight_kg <= $w + 1e-9) return $i;
    }
    return null;
}

/** Последняя миля курьера КСЕ из матрицы, EUR. null если зона/вес недопустимы. */
function last_mile_courier_eur(float $weight_kg, int $zone): ?float {
    $courier = courier_tariffs()['courier'];
    if (!array_key_exists($zone, $courier)) return null;
    $bi = courier_bracket_index($weight_kg);
    if ($bi === null) return null;
    return (float)$courier[$zone][$bi];
}

/**
 * Полный расчёт курьерской доставки КСЕ. Структура ответа как у calc().
 * ok=false, если вес > 31 кг или неизвестная зона.
 */
function calc_courier(float $weight_kg, int $zone): array {
    $weight_kg = max($weight_kg, 0.0);
    if ($weight_kg > MAX_COURIER_WEIGHT_KG) {
        return ['ok' => false,
                'reason' => "вес {$weight_kg} кг > " . MAX_COURIER_WEIGHT_KG . " кг (лимит курьера)",
                'weight_kg' => $weight_kg, 'zone' => $zone];
    }
    $lm = last_mile_courier_eur($weight_kg, $zone);
    if ($lm === null) {
        return ['ok' => false, 'reason' => "нет курьерского тарифа для зоны {$zone}",
                'weight_kg' => $weight_kg, 'zone' => $zone];
    }
    $lh  = LINEHAUL_EUR_PER_KG * $weight_kg;
    $eur = $lh + $lm;
    $rub = (int)round($eur * EUR_RUB);
    return [
        'ok'           => true,
        'rub'          => $rub,
        'eur'          => round($eur, 2),
        'linehaul_eur' => round($lh, 2),
        'lastmile_eur' => round($lm, 2),
        'weight_kg'    => $weight_kg,
        'zone'         => $zone,
        'reason'       => null,
    ];
}

// Запуск из консоли для самопроверки: php tariff.php
if (PHP_SAPI === 'cli' && isset($argv[0]) && realpath($argv[0]) === __FILE__) {
    foreach ([[1,1],[2,5],[0.4,1],[5,10],[15,10],[16,1],[3,99]] as $c) {
        echo $c[0] . " кг зона " . $c[1] . " -> " . json_encode(calc((float)$c[0], (int)$c[1]), JSON_UNESCAPED_UNICODE) . "\n";
    }
    echo "--- курьер КСЕ ---\n";
    foreach ([[1,1],[2,5],[5,10],[31,10],[32,1],[3,99]] as $c) {
        echo $c[0] . " кг зона " . $c[1] . " -> " . json_encode(calc_courier((float)$c[0], (int)$c[1]), JSON_UNESCAPED_UNICODE) . "\n";
    }
}
