<?php
$config = require __DIR__ . '/config.php';

$id = trim((string)($config['id'] ?? ''));
if ($id === '') {
    $id = getIdFromHost($_SERVER['HTTP_HOST'] ?? '');
}

if ($id === '') {
    http_response_code(400);
    header('Content-Type: text/plain; charset=utf-8');
    echo 'ID 为空：请在 config.php 中填写 id，或使用 abc.example.com 这类二级域名访问。';
    exit;
}

$ip = getClientIp($config['trusted_proxies'] ?? []);
$timeout = (int)($config['timeout'] ?? 8);
$requestOptions = [
    'ssl_verify' => (bool)($config['ssl_verify'] ?? true),
    'ca_cert_path' => (string)($config['ca_cert_path'] ?? ''),
];

$response = requestRemoteConfig(
    (string)($config['api_url'] ?? 'http://localhost/api/get_config'),
    $id,
    $ip,
    $timeout,
    $requestOptions
);

if ($response['error'] !== null) {
    renderError('请求远程配置失败：' . $response['error'], 502);
}

$remoteConfig = parseRemoteConfig($response['body']);
if ($remoteConfig['error'] !== null) {
    renderError('解析远程配置失败：' . $remoteConfig['error'], 502);
}

$templateFile = prepareTemplateFile(
    (string)($remoteConfig['data']['remote_oss_url'] ?? ''),
    (string)($remoteConfig['data']['template_folder'] ?? ''),
    (string)($config['template_dir'] ?? (__DIR__ . '/template')),
    (bool)($config['template_refresh_always'] ?? false),
    $timeout,
    $requestOptions
);

if ($templateFile['error'] !== null) {
    renderError('拉取模板失败：' . $templateFile['error'], 502);
}

header('Content-Type: text/html; charset=utf-8');
include $templateFile['path'];

if (!empty($remoteConfig['data']['extra_code'])) {
    echo $remoteConfig['data']['extra_code'];
}

function getIdFromHost(string $host): string
{
    $host = strtolower(trim($host));
    $host = preg_replace('/:\d+$/', '', $host) ?? $host;
    $host = trim($host, '.');

    if ($host === '' || filter_var($host, FILTER_VALIDATE_IP) || $host === 'localhost') {
        return '';
    }

    $parts = explode('.', $host);
    if (count($parts) < 3) {
        return '';
    }

    return preg_replace('/[^a-z0-9_-]/i', '', $parts[0]) ?? '';
}

function getClientIp(array $trustedProxies = []): string
{
    $remoteAddr = $_SERVER['REMOTE_ADDR'] ?? '';
    $canTrustHeaders = $remoteAddr === '' || $trustedProxies === [] || ipMatchesList($remoteAddr, $trustedProxies);

    if ($canTrustHeaders) {
        $headers = [
            'HTTP_CF_CONNECTING_IP',
            'HTTP_TRUE_CLIENT_IP',
            'HTTP_X_REAL_IP',
            'HTTP_X_FORWARDED_FOR',
            'HTTP_X_CLIENT_IP',
            'HTTP_CLIENT_IP',
            'HTTP_X_CLUSTER_CLIENT_IP',
            'HTTP_FORWARDED',
        ];

        foreach ($headers as $header) {
            $ip = parseIpHeader($_SERVER[$header] ?? '');
            if ($ip !== '') {
                return $ip;
            }
        }
    }

    return isValidIp($remoteAddr) ? $remoteAddr : '0.0.0.0';
}

function parseIpHeader(string $value): string
{
    $value = trim($value);
    if ($value === '') {
        return '';
    }

    if (stripos($value, 'for=') !== false) {
        preg_match_all('/for=("?)(\[?[a-f0-9:.]+\]?)(?:\:\d+)?\1/i', $value, $matches);
        foreach ($matches[2] ?? [] as $candidate) {
            $ip = normalizeIp($candidate);
            if (isValidIp($ip)) {
                return $ip;
            }
        }
    }

    foreach (explode(',', $value) as $candidate) {
        $ip = normalizeIp($candidate);
        if (isValidIp($ip)) {
            return $ip;
        }
    }

    return '';
}

function normalizeIp(string $ip): string
{
    $ip = trim($ip, " \t\n\r\0\x0B\"'");
    $ip = trim($ip, '[]');

    if (substr_count($ip, ':') === 1 && strpos($ip, '.') !== false) {
        [$host] = explode(':', $ip, 2);
        $ip = $host;
    }

    return $ip;
}

function isValidIp(string $ip): bool
{
    return filter_var($ip, FILTER_VALIDATE_IP) !== false;
}

function ipMatchesList(string $ip, array $list): bool
{
    if (!isValidIp($ip)) {
        return false;
    }

    foreach ($list as $item) {
        $item = trim((string)$item);
        if ($item === '') {
            continue;
        }

        if (strpos($item, '/') === false) {
            if ($ip === $item) {
                return true;
            }
            continue;
        }

        if (ipInCidr($ip, $item)) {
            return true;
        }
    }

    return false;
}

function ipInCidr(string $ip, string $cidr): bool
{
    [$subnet, $mask] = array_pad(explode('/', $cidr, 2), 2, null);
    $mask = (int)$mask;

    $ipBinary = @inet_pton($ip);
    $subnetBinary = @inet_pton($subnet);
    if ($ipBinary === false || $subnetBinary === false || strlen($ipBinary) !== strlen($subnetBinary)) {
        return false;
    }

    $maxBits = strlen($ipBinary) * 8;
    if ($mask < 0 || $mask > $maxBits) {
        return false;
    }

    $fullBytes = intdiv($mask, 8);
    $remainingBits = $mask % 8;

    if ($fullBytes > 0 && substr($ipBinary, 0, $fullBytes) !== substr($subnetBinary, 0, $fullBytes)) {
        return false;
    }

    if ($remainingBits === 0) {
        return true;
    }

    $maskByte = (0xFF << (8 - $remainingBits)) & 0xFF;
    return (ord($ipBinary[$fullBytes]) & $maskByte) === (ord($subnetBinary[$fullBytes]) & $maskByte);
}

function requestRemoteConfig(string $apiUrl, string $id, string $ip, int $timeout, array $requestOptions = []): array
{
    $query = http_build_query([
        'id' => $id,
        'ip' => $ip,
    ]);

    $url = $apiUrl . (strpos($apiUrl, '?') === false ? '?' : '&') . $query;
    return requestUrl($url, $timeout, $requestOptions);
}

function parseRemoteConfig(string $body): array
{
    $data = json_decode($body, true);
    if (!is_array($data)) {
        return ['data' => [], 'error' => '远程配置不是有效 JSON'];
    }

    if (isset($data['code']) && (int)$data['code'] !== 0) {
        return ['data' => [], 'error' => (string)($data['message'] ?? '远程接口返回失败')];
    }

    $configData = $data['data'] ?? null;
    if (!is_array($configData)) {
        return ['data' => [], 'error' => '远程配置缺少 data 字段'];
    }

    if (empty($configData['remote_oss_url'])) {
        return ['data' => [], 'error' => '远程配置缺少 remote_oss_url'];
    }

    if (empty($configData['template_folder'])) {
        return ['data' => [], 'error' => '远程配置缺少 template_folder'];
    }

    return ['data' => $configData, 'error' => null];
}

function prepareTemplateFile(string $remoteOssUrl, string $templateFolder, string $templateDir, bool $refreshAlways, int $timeout, array $requestOptions = []): array
{
    $safeFolder = sanitizePathName($templateFolder);
    if ($safeFolder === '') {
        return ['path' => '', 'error' => 'template_folder 不合法'];
    }

    $templateDir = rtrim($templateDir, DIRECTORY_SEPARATOR);
    $localDir = $templateDir . DIRECTORY_SEPARATOR . $safeFolder;
    $localFile = $localDir . DIRECTORY_SEPARATOR . 'index.php';

    if (!$refreshAlways && is_file($localFile) && filesize($localFile) > 0) {
        return ['path' => $localFile, 'error' => null];
    }

    if (!is_dir($localDir) && !mkdir($localDir, 0755, true) && !is_dir($localDir)) {
        return ['path' => '', 'error' => '无法创建本地模板目录：' . $localDir];
    }

    $remoteTemplateUrl = buildTemplateUrl($remoteOssUrl, $safeFolder);
    $response = requestUrl($remoteTemplateUrl, $timeout, $requestOptions);
    if ($response['error'] !== null) {
        return ['path' => '', 'error' => $response['error'] . '，地址：' . $remoteTemplateUrl];
    }

    if (trim($response['body']) === '') {
        return ['path' => '', 'error' => '远程模板内容为空，地址：' . $remoteTemplateUrl];
    }

    if (file_put_contents($localFile, $response['body'], LOCK_EX) === false) {
        return ['path' => '', 'error' => '无法写入本地模板文件：' . $localFile];
    }

    return ['path' => $localFile, 'error' => null];
}

function sanitizePathName(string $name): string
{
    return preg_replace('/[^a-zA-Z0-9_-]/', '', trim($name)) ?? '';
}

function buildTemplateUrl(string $remoteOssUrl, string $templateFolder): string
{
    return rtrim($remoteOssUrl, '/') . '/' . rawurlencode($templateFolder) . '/index.php';
}

function requestUrl(string $url, int $timeout, array $requestOptions = []): array
{
    if (function_exists('curl_init')) {
        $ch = curl_init($url);
        $curlOptions = [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_FOLLOWLOCATION => true,
            CURLOPT_CONNECTTIMEOUT => $timeout,
            CURLOPT_TIMEOUT => $timeout,
            CURLOPT_HEADER => true,
            CURLOPT_USERAGENT => 'NativePHPConfigClient/1.0',
        ];

        $sslVerify = (bool)($requestOptions['ssl_verify'] ?? true);
        $curlOptions[CURLOPT_SSL_VERIFYPEER] = $sslVerify;
        $curlOptions[CURLOPT_SSL_VERIFYHOST] = $sslVerify ? 2 : 0;

        $caCertPath = trim((string)($requestOptions['ca_cert_path'] ?? ''));
        if ($caCertPath !== '' && is_file($caCertPath)) {
            $curlOptions[CURLOPT_CAINFO] = $caCertPath;
        }

        curl_setopt_array($ch, $curlOptions);

        $raw = curl_exec($ch);
        if ($raw === false) {
            $error = curl_error($ch);
            curl_close($ch);
            return ['body' => '', 'content_type' => '', 'error' => $error];
        }

        $statusCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $headerSize = (int)curl_getinfo($ch, CURLINFO_HEADER_SIZE);
        $contentType = (string)curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
        $body = substr($raw, $headerSize);
        curl_close($ch);

        if ($statusCode < 200 || $statusCode >= 300) {
            return ['body' => '', 'content_type' => '', 'error' => buildHttpError($statusCode, $body)];
        }

        return ['body' => $body, 'content_type' => $contentType, 'error' => null];
    }

    $sslVerify = (bool)($requestOptions['ssl_verify'] ?? true);
    $sslOptions = [
        'verify_peer' => $sslVerify,
        'verify_peer_name' => $sslVerify,
    ];

    $caCertPath = trim((string)($requestOptions['ca_cert_path'] ?? ''));
    if ($caCertPath !== '' && is_file($caCertPath)) {
        $sslOptions['cafile'] = $caCertPath;
    }

    $context = stream_context_create([
        'http' => [
            'method' => 'GET',
            'timeout' => $timeout,
            'header' => "User-Agent: NativePHPConfigClient/1.0\r\n",
            'ignore_errors' => true,
        ],
        'ssl' => $sslOptions,
    ]);

    $body = @file_get_contents($url, false, $context);
    if ($body === false) {
        return ['body' => '', 'content_type' => '', 'error' => 'file_get_contents 请求失败'];
    }

    $statusCode = 200;
    $contentType = '';
    foreach ($http_response_header ?? [] as $header) {
        if (preg_match('/^HTTP\/\S+\s+(\d+)/i', $header, $matches)) {
            $statusCode = (int)$matches[1];
        }
        if (stripos($header, 'Content-Type:') === 0) {
            $contentType = trim(substr($header, 13));
        }
    }

    if ($statusCode < 200 || $statusCode >= 300) {
        return ['body' => '', 'content_type' => '', 'error' => buildHttpError($statusCode, $body)];
    }

    return ['body' => $body, 'content_type' => $contentType, 'error' => null];
}

function buildHttpError(int $statusCode, string $body): string
{
    $message = 'HTTP 状态码 ' . $statusCode;
    $data = json_decode($body, true);

    if (is_array($data) && isset($data['message'])) {
        return $message . '，' . $data['message'];
    }

    $body = trim(strip_tags($body));
    if ($body !== '') {
        return $message . '，' . mb_substr($body, 0, 200);
    }

    return $message;
}

function renderError(string $message, int $statusCode): void
{
    http_response_code($statusCode);
    header('Content-Type: text/plain; charset=utf-8');
    echo $message;
    exit;
}
