<?php
declare(strict_types=1);
@ini_set('display_errors','0'); @ini_set('log_errors','1'); @error_reporting(E_ALL);

/* ================== НАСТРОЙКИ ================== */
const REQUIRE_HTTPS         = false;                 // при желании включи редирект на https
const TOKEN_COOKIE_TTL_DAYS = 7;                     // срок жизни куки после успешного токена
const COOKIE_PREFIX         = 'dlk_';                // префикс имени куки по каналу
const FORCE_DOWNLOAD        = true;                  // Content-Disposition: attachment
const KIT_MAX               = 10;                    // количество комплектов (k1..k10)

// Каналы → БАЗОВЫЕ имена папок (лежат рядом с index.php)
// ВАЖНО: физическая папка для канала 'hv' называется 'xv'
const CHANNEL_DIRS = [
    'st'   => __DIR__ . '/st',
    'hv'   => __DIR__ . '/hv',   // логический канал hv хранится в папке xv
    'flow' => __DIR__ . '/flow',
];

// Разрешённые токены (по каналам): сразу зашиты 10 штук на канал (пример).
// Можно заменить своими значениями. Суффикс _kN определяет комплект и папку.
const TOKENS = [
    'st'   => ['st_k1','st_k2','st_k3','st_k4','st_k5','st_k6','st_k7','st_k8','st_k9','st_k10'],
    'hv'   => ['hv_k1','hv_k2','hv_k3','hv_k4','hv_k5','hv_k6','hv_k7','hv_k8','hv_k9','hv_k10'],
    'flow' => ['flow_k1','flow_k2','flow_k3','flow_k4','flow_k5','flow_k6','flow_k7','flow_k8','flow_k9','flow_k10'],
    '_'    => [ /* глобальные токены, если надо, напр. 'all_k3' */ ],
];

/**
 * СПЕЦКАРТА: конкретный токен → конкретная ПАПКА (относительно корня скрипта).
 * Токен 'st1_gate' для канала 'st' ведёт в './st1'.
 */
const SPECIAL_TOKEN_DIRS = [
    'st' => [
        'st1_gate' => 'st1', // /st?k=st1_gate → __DIR__/st1
    ],
    '_'  => [
        // 'all_shared_k3' => 'shared3',
    ],
];

/* ================ ВСПОМОГАТЕЛЬНЫЕ ================ */
function require_https(): void {
    if (REQUIRE_HTTPS && (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] === 'off')) {
        $host = $_SERVER['HTTP_HOST'] ?? 'localhost';
        $uri  = $_SERVER['REQUEST_URI'] ?? '/';
        header('Location: https://' . $host . $uri, true, 301);
        exit;
    }
}
function json_error(int $code, string $msg, array $extra = []): void {
    http_response_code($code);
    header('Content-Type: application/json; charset=utf-8');
    echo json_encode(['ok' => false, 'code' => $code, 'error' => $msg] + $extra, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    exit;
}
function get_channel(): string {
    $ch = $_GET['ch'] ?? '';
    if (!isset(CHANNEL_DIRS[$ch])) {
        json_error(404, 'unknown_channel', ['hint' => 'use /st, /hv or /flow']);
    }
    return $ch;
}
function cookie_name(string $ch): string { return COOKIE_PREFIX . $ch; }

function fetch_token(string $ch): ?string {
    // Приоритет: ?k=..., затем заголовок X-Token, затем кука по каналу
    $q = trim((string)($_GET['k'] ?? ''));
    if ($q !== '') return $q;

    if (function_exists('getallheaders')) {
        $hdrs = getallheaders();
        if (!empty($hdrs['X-Token'])) return trim($hdrs['X-Token']);
        if (!empty($hdrs['x-token'])) return trim($hdrs['x-token']);
    } else {
        $possible = $_SERVER['HTTP_X_TOKEN'] ?? ($_SERVER['X_TOKEN'] ?? '');
        if (trim((string)$possible) !== '') return trim((string)$possible);
    }
    $ck = $_COOKIE[cookie_name($ch)] ?? '';
    return $ck !== '' ? trim($ck) : null;
}

/** Все валидные токены для канала с учётом спецключей (без хвостовых запятых — совместимо с PHP < 7.3). */
function tokens_for_channel(string $ch): array {
    $base         = TOKENS['_'] ?? [];
    $chan         = TOKENS[$ch] ?? [];
    $specialG     = array_keys(SPECIAL_TOKEN_DIRS['_']  ?? []);
    $specialCh    = array_keys(SPECIAL_TOKEN_DIRS[$ch]  ?? []);
    return array_merge($base, $chan, $specialG, $specialCh);
}

function token_valid(string $ch, ?string $t): bool {
    if ($t === null || $t === '') return false;
    foreach (tokens_for_channel($ch) as $v) {
        if (hash_equals($v, $t)) return true;
    }
    return false;
}

/** Комплект из токена: ждём суффикс _k1.._k10 (регистр не важен). Иначе — k1. */
function kit_from_token(?string $token): int {
    if ($token && preg_match('/(^|_)k(\d{1,2})$/i', $token, $m)) {
        $n = (int)$m[2];
        if ($n >= 1 && $n <= KIT_MAX) return $n;
    }
    return 1;
}

/**
 * Если токен объявлен в SPECIAL_TOKEN_DIRS → вернуть полный путь к папке-override.
 * Возвращает null, если переопределения нет.
 */
function special_dir_for_token(string $ch, ?string $token): ?string {
    if (!$token) return null;
    $mapGlobal  = SPECIAL_TOKEN_DIRS['_'] ?? [];
    $mapChannel = SPECIAL_TOKEN_DIRS[$ch] ?? [];
    $map        = $mapGlobal + $mapChannel; // ключи-строки, порядок не критичен
    if (!isset($map[$token])) return null;

    $dirName = (string)$map[$token];
    // Санитайз имени папки (без '../' и т.п.)
    if (!preg_match('/^[A-Za-z0-9._-]+$/', $dirName)) return null;

    return __DIR__ . '/' . $dirName;
}

function remember_token(string $ch, string $t): void {
    $expires = time() + TOKEN_COOKIE_TTL_DAYS * 86400;
    $secure  = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off';
    $opts = ['expires'=>$expires,'path'=>'/','secure'=>$secure,'httponly'=>true,'samesite'=>'Lax'];
    @setcookie(cookie_name($ch), $t, $opts);
}

/**
 * Возвращает ПОЛНЫЙ путь к папке хранения для канала и комплекта (плоско в корне).
 * k1 → базовая папка (как раньше):  st, xv, flow
 * k2..k10 → корневые папки с цифрой: st2, xv2, flow2 ... st10, xv10, flow10
 */
function storage_dir_for(string $ch, int $kit): string {
    // Базовое имя папки в корне: для hv — 'xv', для остальных — как есть
    $baseName = ($ch === 'hv') ? 'hv' : $ch;

    if ($kit <= 1) {
        // Совместимость: k1 — без цифры, ровно как раньше
        $dirName = $baseName;
    } else {
        // k2..k10 — плоские папки в корне с номером комплекта
        $dirName = $baseName . (string)$kit; // st2, xv3, flow10
    }
    return __DIR__ . '/' . $dirName;
}

function latest_file(string $dir): ?string {
    if (!is_dir($dir)) return null;
    $dh = @opendir($dir);
    if (!$dh) return null;
    $latest = null; $latest_m = -1;
    while (($e = readdir($dh)) !== false) {
        if ($e === '.' || $e === '..' || $e === '.htaccess') continue;
        $p = $dir . DIRECTORY_SEPARATOR . $e;
        if (!is_file($p)) continue;
        $m = @filemtime($p) ?: 0;
        if ($m >= $latest_m) { $latest_m = $m; $latest = $p; }
    }
    closedir($dh);
    return $latest;
}
function mime_of(string $path): string {
    if (function_exists('mime_content_type')) {
        $m = @mime_content_type($path); if ($m) return $m;
    }
    if (function_exists('finfo_open')) {
        $fi = @finfo_open(FILEINFO_MIME_TYPE);
        if ($fi) { $m = @finfo_file($fi, $path); @finfo_close($fi); if ($m) return $m; }
    }
    return 'application/octet-stream';
}
function send_file(string $path): void {
    if (!is_readable($path)) json_error(404, 'file_not_readable');

    @set_time_limit(0);
    @clearstatcache(true, $path);
    $size = @filesize($path);
    $name = basename($path);
    $mime = mime_of($path);

    header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
    header('Pragma: no-cache');
    header('Expires: 0');

    header('Content-Type: ' . $mime);
    header('Content-Disposition: ' . (FORCE_DOWNLOAD ? 'attachment' : 'inline') . '; filename="' . rawurlencode($name) . '"');
    if ($size !== false) header('Content-Length: ' . $size);

    if (strtoupper($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'HEAD') exit;

    $fp = @fopen($path, 'rb');
    if (!$fp) json_error(500, 'fopen_failed');
    while (!feof($fp)) {
        $buf = fread($fp, 8192);
        if ($buf === false) break;
        echo $buf;
        flush();
    }
    fclose($fp);
    exit;
}

/* ================== ОСНОВНОЙ ПОТОК ================== */
require_https();

$ch    = get_channel();
$token = fetch_token($ch);
if (!token_valid($ch, $token)) {
    json_error(403, 'invalid_token', [
        'channel' => $ch,
        'hint'    => 'pass token via ?k=YOUR_TOKEN or X-Token header'
    ]);
}
$kit = kit_from_token($token);
remember_token($ch, $token);

// 1) Если токен — спецключ → жёстко берём папку из SPECIAL_TOKEN_DIRS
$overrideDir = special_dir_for_token($ch, $token);

// 2) Иначе стандартная логика комплектов k1..k10
$dir  = $overrideDir ?? storage_dir_for($ch, $kit);

$file = latest_file($dir);
if ($file === null) {
    json_error(404, 'no_files_in_channel', ['channel' => $ch, 'kit' => $kit, 'dir' => $dir]);
}

send_file($file);
