<?php
declare(strict_types=1);

/**
 * API 映射层入口：
 * - 接收 /api/* 请求
 * - 转发到配置的上游新接口
 * - 必要时把返回转换成旧接口期望结构（主要是 user/index 的 data 结构）
 */

header('Content-Type: application/json; charset=utf-8');

if (!function_exists('str_starts_with')) {
    function str_starts_with(string $haystack, string $needle): bool {
        return $needle === '' || strncmp($haystack, $needle, strlen($needle)) === 0;
    }
}

function json_response(int $httpCode, array $payload): void {
    http_response_code($httpCode);
    echo json_encode($payload, JSON_UNESCAPED_UNICODE);
    exit;
}

function load_config(): array {
    $cfgFile = __DIR__ . '/../config/config.php';
    if (is_file($cfgFile)) {
        $cfg = require $cfgFile;
        if (is_array($cfg)) return $cfg;
    }
    $example = __DIR__ . '/../config/config.example.php';
    $cfg = is_file($example) ? (require $example) : [];
    return is_array($cfg) ? $cfg : [];
}

function mapper_debug_enabled(array $cfg): bool {
    $env = getenv('MAPPER_DEBUG');
    if (is_string($env) && trim($env) !== '') {
        return in_array(strtolower(trim($env)), ['1', 'true', 'yes', 'on'], true);
    }
    return !empty($cfg['debug_log']);
}

function mapper_log(array $cfg, string $line): void {
    if (!mapper_debug_enabled($cfg)) return;
    $dir = __DIR__ . '/../logs';
    if (!is_dir($dir)) {
        @mkdir($dir, 0777, true);
    }
    $file = $dir . '/mapper.log';
    $ts = date('Y-m-d H:i:s');
    @file_put_contents($file, "[$ts] $line\n", FILE_APPEND);
}

function summarize_json_structure($val): array {
    $t = gettype($val);
    if (is_array($val)) {
        $isList = array_key_exists(0, $val);
        $keys = $isList ? [] : array_slice(array_keys($val), 0, 30);
        $first = null;
        if ($isList && count($val) > 0) $first = $val[0];
        return [
            'type' => $isList ? 'list' : 'dict',
            'count' => $isList ? count($val) : count($val),
            'keys' => $keys,
            'first_type' => $first === null ? null : gettype($first),
            'first_keys' => is_array($first) ? array_slice(array_keys($first), 0, 40) : null,
        ];
    }
    return ['type' => $t];
}

function upstream_base_from_config(array $cfg): string {
    // 1) 环境变量优先（适合容器/宝塔面板环境变量）
    $env = getenv('UPSTREAM_API_BASE');
    if (is_string($env) && trim($env) !== '') {
        return rtrim(trim($env), '/');
    }

    // 2) 按 Host 匹配
    $host = $_SERVER['HTTP_HOST'] ?? '';
    $host = strtolower(trim(explode(':', $host)[0]));
    if ($host !== '' && isset($cfg['upstreams']) && is_array($cfg['upstreams']) && isset($cfg['upstreams'][$host])) {
        $u = (string)$cfg['upstreams'][$host];
        if (trim($u) !== '') return rtrim(trim($u), '/');
    }

    // 3) 默认上游
    if (isset($cfg['default_upstream']) && is_string($cfg['default_upstream']) && trim($cfg['default_upstream']) !== '') {
        return rtrim(trim($cfg['default_upstream']), '/');
    }

    return '';
}

function get_request_path(): string {
    $uri = $_SERVER['REQUEST_URI'] ?? '/';
    $path = parse_url($uri, PHP_URL_PATH);
    if (!is_string($path)) return '/';
    return $path;
}

function read_json_body(): array {
    $raw = file_get_contents('php://input');
    if (!is_string($raw) || trim($raw) === '') return [];
    $decoded = json_decode($raw, true);
    return is_array($decoded) ? $decoded : [];
}

function read_post_fallback(): array {
    // 若客户端是 form-urlencoded
    if (!empty($_POST) && is_array($_POST)) return $_POST;
    return [];
}

function curl_post_json(string $url, array $payload, int $timeoutSec): array {
    $ch = curl_init($url);
    $headers = [
        'Content-Type: application/json',
        'Accept: application/json',
    ];
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload, JSON_UNESCAPED_UNICODE));
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeoutSec);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeoutSec);
    // 上游如果是自签证书可改为 false；建议你线上保持 true
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
    curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);

    $respBody = curl_exec($ch);
    $errNo = curl_errno($ch);
    $errStr = curl_error($ch);
    $httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($errNo !== 0) {
        return [
            'ok' => false,
            'http' => 502,
            'error' => "curl_error({$errNo}): {$errStr}",
            'raw' => null,
        ];
    }

    $json = null;
    if (is_string($respBody) && trim($respBody) !== '') {
        $decoded = json_decode($respBody, true);
        if (is_array($decoded)) $json = $decoded;
    }

    return [
        'ok' => ($httpCode >= 200 && $httpCode < 300),
        'http' => $httpCode ?: 200,
        'error' => null,
        'raw' => $respBody,
        'json' => $json,
    ];
}

function normalize_code_to_string(array &$resp): void {
    // 你的客户端经常用 str(code) == '0' 做判断：这里统一一下
    if (array_key_exists('code', $resp) && $resp['code'] !== null) {
        $resp['code'] = (string)$resp['code'];
    }
}

function decode_data_field(array &$resp): void {
    if (!array_key_exists('data', $resp)) return;
    $data = $resp['data'];

    // data 本身是 JSON 字符串
    if (is_string($data)) {
        $decoded = json_decode($data, true);
        if (is_array($decoded)) {
            $data = $decoded;
        }
    }

    // data 是数组，但每个元素可能又是 JSON 字符串
    if (is_array($data)) {
        foreach ($data as $i => $item) {
            if (!is_string($item)) continue;
            $decodedItem = json_decode($item, true);
            if (is_array($decodedItem)) $data[$i] = $decodedItem;
        }
    }

    $resp['data'] = $data;
}

function transform_user_index(array $resp): array {
    // 旧：data 为 object；新：data 为 array[0]
    $data = $resp['data'] ?? null;
    if (is_array($data)) {
        // 如果是列表形式，取第一项
        $isList = array_key_exists(0, $data);
        if ($isList && is_array($data[0])) {
            $obj = $data[0];
            // 旧版可能还有 group_name，新版没有就补空
            if (!array_key_exists('group_name', $obj)) {
                $obj['group_name'] = '';
            }
            $resp['data'] = $obj;
        }
    }
    normalize_code_to_string($resp);
    return $resp;
}

function transform_lipin_list(array $resp): array {
    // 尽量不改，只做必要的字段兼容（比如 id/kuaidi_id 命名差异）
    $data = $resp['data'] ?? null;

    // 有些上游会把 data 返回成 JSON 字符串：这里尝试解码
    if (is_string($data)) {
        $decoded = json_decode($data, true);
        if (is_array($decoded)) $data = $decoded;
    }

    // data 可能是“对象/字典”（关联数组），客户端期望 list：
    // 关键点：要保留 key（通常就是 kuaidi_id），否则客户端用 item['id'] 请求 goods 会拿不到商品。
    if (is_array($data) && !array_key_exists(0, $data)) {
        $newData = [];
        foreach ($data as $k => $v) {
            // 1) value 是数组：尽量直接用，并把 key 回填为 id/kuaidi_id
            if (is_array($v)) {
                if (!isset($v['id']) && $k !== '') $v['id'] = (string)$k;
                if (!isset($v['kuaidi_id']) && $k !== '') $v['kuaidi_id'] = (string)$k;
                $newData[] = $v;
                continue;
            }

            // 2) value 是字符串：常见于 { "12": "圆通" } 这种
            if (is_string($v)) {
                $name = trim($v);
                $kid = (string)$k;
                if ($name !== '' || $kid !== '') {
                    $newData[] = [
                        'name' => $name !== '' ? $name : $kid,
                        'id' => $kid !== '' ? $kid : ($name !== '' ? $name : ''),
                        'kuaidi_id' => $kid !== '' ? $kid : ($name !== '' ? $name : ''),
                        'price' => '0',
                        'subname' => '',
                        'is_shgl' => '',
                        'area_config' => '',
                    ];
                }
                continue;
            }
        }
        $data = $newData;
    }

    if (is_array($data)) {
        foreach ($data as $i => $item) {
            // 有些上游会把每个 item 也变成 JSON 字符串
            if (is_string($item)) {
                $decodedItem = json_decode($item, true);
                if (is_array($decodedItem)) {
                    $item = $decodedItem;
                } else {
                    // item 是纯字符串（例如只返回快递名），包装成客户端可用结构，避免 .get() 崩溃
                    $name = trim($item);
                    if ($name !== '') {
                        $item = [
                            'name' => $name,
                            // 有些上游把“name”当作 id 使用；这里用 name 兜底，至少不让客户端崩
                            'id' => $name,
                            'kuaidi_id' => $name,
                            'price' => '0',
                            'subname' => '',
                            'is_shgl' => '',
                            'area_config' => '',
                        ];
                    }
                }
            }

            if (!is_array($item)) continue;

            // 新接口如果返回 kuaidi_id，而旧接口/客户端期望 id，则补 id
            if (!isset($item['id']) && isset($item['kuaidi_id'])) {
                $item['id'] = $item['kuaidi_id'];
            }
            // 反过来：如果只有 id，补 kuaidi_id（某些客户端/后续接口会用到）
            if (!isset($item['kuaidi_id']) && isset($item['id'])) {
                $item['kuaidi_id'] = $item['id'];
            }
            $data[$i] = $item;
        }
        $resp['data'] = $data;
    }
    normalize_code_to_string($resp);
    return $resp;
}

function transform_lipin_goods(array $resp): array {
    // 商品列表字段兜底：客户端期望 goods_id / goods_name / price / goods_pic
    $data = $resp['data'] ?? null;
    if (is_string($data)) {
        $decoded = json_decode($data, true);
        if (is_array($decoded)) $data = $decoded;
    }
    if (is_array($data) && !array_key_exists(0, $data)) {
        $data = array_values($data);
    }

    if (is_array($data)) {
        foreach ($data as $i => $g) {
            if (is_string($g)) {
                $dg = json_decode($g, true);
                if (is_array($dg)) $g = $dg;
            }
            if (!is_array($g)) continue;

            if (!isset($g['goods_id'])) {
                if (isset($g['id'])) $g['goods_id'] = $g['id'];
                else if (isset($g['gid'])) $g['goods_id'] = $g['gid'];
            }
            if (!isset($g['goods_name'])) {
                if (isset($g['name'])) $g['goods_name'] = $g['name'];
                else if (isset($g['title'])) $g['goods_name'] = $g['title'];
            }
            if (!isset($g['price'])) {
                if (isset($g['money'])) $g['price'] = $g['money'];
                else if (isset($g['cost'])) $g['price'] = $g['cost'];
            }
            if (!isset($g['goods_pic'])) {
                if (isset($g['pic'])) $g['goods_pic'] = $g['pic'];
                else if (isset($g['image'])) $g['goods_pic'] = $g['image'];
                else if (isset($g['img'])) $g['goods_pic'] = $g['img'];
            }

            $data[$i] = $g;
        }
        $resp['data'] = $data;
    }

    // 注意：你的客户端对 goods 的判断是 `code == 0`（数字比较），这里保持为 int
    if (array_key_exists('code', $resp) && is_numeric($resp['code'])) {
        $resp['code'] = (int)$resp['code'];
    }
    return $resp;
}

function maybe_transform(string $urlPart, array $resp): array {
    // 先把 data 的“字符串 JSON”兜底解码掉，避免客户端拿到 str 直接 .get() 崩溃
    decode_data_field($resp);

    // 只对少数接口做结构变换，其它透传
    if ($urlPart === 'user/index') return transform_user_index($resp);
    if ($urlPart === 'lipin/list') return transform_lipin_list($resp);
    if ($urlPart === 'lipin/goods') return transform_lipin_goods($resp);
    normalize_code_to_string($resp);
    return $resp;
}

// ------------------ main ------------------
$cfg = load_config();
$upstreamBase = upstream_base_from_config($cfg);
if ($upstreamBase === '') {
    json_response(500, [
        'code' => '500',
        'msg' => '映射层未配置上游 UPSTREAM_API_BASE（或 config/config.php default_upstream）',
        'data' => [],
    ]);
}

$timeout = 12;
if (isset($cfg['timeout_seconds'])) $timeout = max(3, (int)$cfg['timeout_seconds']);

$path = get_request_path(); // 例如 /api/lipin/list
if (!str_starts_with($path, '/api/')) {
    json_response(404, ['code' => '404', 'msg' => 'Not Found', 'data' => []]);
}

$urlPart = ltrim(substr($path, strlen('/api/')), '/'); // lipin/list
if ($urlPart === '') {
    json_response(404, ['code' => '404', 'msg' => 'Not Found', 'data' => []]);
}

// 只支持 POST（与你的软件一致）
$method = $_SERVER['REQUEST_METHOD'] ?? 'GET';
if (strtoupper($method) !== 'POST') {
    json_response(405, ['code' => '405', 'msg' => 'Method Not Allowed', 'data' => []]);
}

// 读取请求体：优先 JSON，否则用 POST 表单
$payload = read_json_body();
if (empty($payload)) {
    $payload = read_post_fallback();
}

$payloadForLog = $payload;
if (is_array($payloadForLog)) {
    // 脱敏：不记录 sign
    if (array_key_exists('sign', $payloadForLog)) $payloadForLog['sign'] = '***';
}
mapper_log($cfg, "REQ {$urlPart} payload_keys=" . (is_array($payloadForLog) ? implode(',', array_keys($payloadForLog)) : ''));

$targetUrl = $upstreamBase . '/' . $urlPart;
$result = curl_post_json($targetUrl, $payload, $timeout);

// cang/cnag 兼容：
// - HTTP 层失败时 fallback
// - 以及 HTTP=200 但 JSON 显示“接口不存在/路由不存在”等，也 fallback
if ($urlPart === 'lipin/cang') {
    $shouldFallback = false;
    if (!$result['ok']) {
        $shouldFallback = true;
    } else if (isset($result['json']) && is_array($result['json'])) {
        $code = (string)($result['json']['code'] ?? '');
        $msg = (string)($result['json']['msg'] ?? '');
        $msgLower = strtolower($msg);
        if ($code !== '' && $code !== '0') {
            if (str_contains($msg, '不存在') || str_contains($msgLower, 'not found') || str_contains($msgLower, 'route') || str_contains($msgLower, '404')) {
                $shouldFallback = true;
            }
        }
    }

    if ($shouldFallback) {
        $fallbackUrl = $upstreamBase . '/lipin/cnag';
        $result2 = curl_post_json($fallbackUrl, $payload, $timeout);
        if ($result2['ok'] || is_array($result2['json'])) {
            $result = $result2;
            $urlPart = 'lipin/cnag';
        }
    }
}

// 没拿到 JSON 时直接回传原文（有些上游会返回纯文本错误页）
if (!isset($result['json']) || !is_array($result['json'])) {
    if (!empty($cfg['passthrough_upstream_error'])) {
        http_response_code((int)($result['http'] ?? 502));
        echo is_string($result['raw'] ?? null) ? $result['raw'] : json_encode(['code' => '502', 'msg' => ($result['error'] ?? 'Bad Gateway'), 'data' => []], JSON_UNESCAPED_UNICODE);
        exit;
    }
    json_response(502, ['code' => '502', 'msg' => ($result['error'] ?? 'Bad Gateway'), 'data' => []]);
}

$resp = $result['json'];
$code = (string)($resp['code'] ?? '');
$msg = (string)($resp['msg'] ?? '');
$dataSummary = summarize_json_structure($resp['data'] ?? null);
mapper_log($cfg, "RESP {$urlPart} code={$code} msg=" . mb_substr($msg, 0, 80) . " data=" . json_encode($dataSummary, JSON_UNESCAPED_UNICODE));
$resp = maybe_transform($urlPart, $resp);
json_response(200, $resp);

