const http = require('http');
const https = require('https');
const url = require('url');
const querystring = require('querystring');
const fs = require('fs');
const pathModule = require('path');
const crypto = require('crypto');
const sharp = require('sharp');

const CACHE_DIR_NAME = '.cache';
const DEFAULT_PORT = 9001;
const DEFAULT_API_ENDPOINT = 'http://183.6.121.121:9005/get/';

const cacheDir = pathModule.join(__dirname, CACHE_DIR_NAME);
const pathIndex = {};

// 访问计数器
const viewsInfo = {
    request: 0,
    cacheHit: 0,
    apiCall: 0,
    cacheCall: 0,
    cacheReadError: 0,
    fetchApiError: 0,
    fetchApiWarning: 0,
    increment: function (key) {
        if (this.hasOwnProperty(key)) {
            this[key]++;
        }
    }
};

let port = DEFAULT_PORT;
let apiEndpoint = DEFAULT_API_ENDPOINT;

// 解析命令行参数函数
function parseArguments() {
    const args = process.argv.slice(2);
    args.forEach(arg => {
        const cleanArg = arg.startsWith('--') ? arg.substring(2) : arg;
        const [key, value] = cleanArg.split('=');
        if (key === 'port' && value) {
            const parsedPort = parseInt(value, 10);
            if (!isNaN(parsedPort)) {
                port = parsedPort;
            }
        } else if (key === 'api' && value) {
            apiEndpoint = value;
        }
    });
}

// 初始化函数,包含参数解析和目录创建
function initializeApp() {
    parseArguments();
    if (!fs.existsSync(cacheDir)) {
        try {
            fs.mkdirSync(cacheDir, { recursive: true });
            console.log(`Cache directory created: ${cacheDir}`);
        } catch (err) {
            console.error(`Error creating cache directory ${cacheDir}:`, err);
            process.exit(1); // Exit if cache directory cannot be created
        }
    }
}

initializeApp();

const CACHE_EXPIRY_MS = 24 * 60 * 60 * 1000; // 24 hours
const CACHE_CLEANUP_INTERVAL_MS = 60 * 60 * 1000; // 1 hour
const HTTP_STATUS = {
    OK: 200,
    NO_CONTENT: 204,
    REDIRECT: 302,
    NOT_MODIFIED: 304,
    BAD_REQUEST: 400,
    NOT_FOUND: 404,
    INTERNAL_SERVER_ERROR: 500,
    BAD_GATEWAY: 502,
};

// 定时清理过期缓存数据
setInterval(() => {
    const currentTime = Date.now();
    for (const key in pathIndex) {
        if (currentTime - pathIndex[key].timestamp > CACHE_EXPIRY_MS) {
            delete pathIndex[key];
            // Consider deleting actual cache files as well if not managed elsewhere
        }
    }
}, CACHE_CLEANUP_INTERVAL_MS);

// 统一发送错误响应
function sendErrorResponse(res, statusCode, message) {
    if (!res.headersSent) {
        res.writeHead(statusCode, { 'Content-Type': 'text/plain;charset=UTF-8' });
        res.end(message);
    }
}

// --- Request Handling Logic ---

async function handleFavicon(req, res) {
    res.writeHead(HTTP_STATUS.NO_CONTENT);
    res.end();
}

async function handleEndpoint(req, res, parsedUrl) {
    if (parsedUrl.query.api) {
        const urlRegex = /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([\/\w.-]*)*\/?$/;
        if (urlRegex.test(parsedUrl.query.api)) {
            apiEndpoint = parsedUrl.query.api;
            console.log(`API endpoint updated to: ${apiEndpoint}`);
        }
    }
    res.writeHead(HTTP_STATUS.OK, { 'Content-Type': 'application/json; charset=utf-8' });
    res.end(JSON.stringify({
        code: HTTP_STATUS.OK,
        data: {
            api: apiEndpoint,
            port: port,
            cacheDir: cacheDir,
            pathIndexCount: Object.keys(pathIndex).length,
            viewsInfo: {
                request: viewsInfo.request,
                cacheHit: viewsInfo.cacheHit,
                apiCall: viewsInfo.apiCall,
                cacheCall: viewsInfo.cacheCall,
                cacheReadError: viewsInfo.cacheReadError,
                fetchApiError: viewsInfo.fetchApiError,
                fetchApiWarning: viewsInfo.fetchApiWarning,
            }
        }
    }));
}

async function handleApiRedirect(res, apiData) {
    res.writeHead(HTTP_STATUS.REDIRECT, { Location: apiData.data.url });
    res.end();
}

async function processSuccessfulApiData(apiData, uniqidhex, reqPath, token, sign, res) {
    const { url: realUrl, cloudtype, expiration, path: apiPath, headers, uniqid, thumb } = apiData.data;
    const data = { realUrl, cloudtype, expiration: expiration * 1000, path: apiPath, headers, uniqid, thumb };

    pathIndex[uniqidhex] = { uniqid: data.uniqid, timestamp: Date.now() };
    const cacheMetaFile = pathModule.join(cacheDir, `${data.uniqid}.meta`);
    const cacheContentFile = pathModule.join(cacheDir, `${data.uniqid}.content`);
    const tempCacheContentFile = pathModule.join(cacheDir, `${data.uniqid}_${crypto.randomBytes(16).toString('hex')}.temp`);

    try {
        fs.writeFileSync(cacheMetaFile, JSON.stringify(data));
    } catch (writeError) {
        console.error(`Error writing meta file ${cacheMetaFile}:`, writeError);
        sendErrorResponse(res, HTTP_STATUS.INTERNAL_SERVER_ERROR, 'Failed to write cache metadata.');
        return;
    }

    if (fs.existsSync(cacheContentFile)) {
        const stats = fs.statSync(cacheContentFile);
        const contentLength = stats.size;
        if (contentLength < 2048 && data.headers['content-length'] && parseInt(data.headers['content-length'], 10) !== contentLength) {
            console.warn(`Content length mismatch for ${cacheContentFile}. API: ${data.headers['content-length']}, Cache: ${contentLength}. Re-fetching.`);
            fetchAndServe(data, tempCacheContentFile, cacheContentFile, cacheMetaFile, res);
        } else {
            serveFromCache(data, cacheContentFile, cacheMetaFile, res);
        }
    } else {
        fetchAndServe(data, tempCacheContentFile, cacheContentFile, cacheMetaFile, res);
    }
}

async function tryServeFromStaleCacheOrError(uniqidhex, res, errorMessage) {
    if (pathIndex[uniqidhex]) {
        const cacheMetaFile = pathModule.join(cacheDir, `${pathIndex[uniqidhex].uniqid}.meta`);
        const cacheContentFile = pathModule.join(cacheDir, `${pathIndex[uniqidhex].uniqid}.content`);
        if (fs.existsSync(cacheMetaFile) && fs.existsSync(cacheContentFile)) {
            console.warn(`API call failed or returned non-200. Serving stale cache for ${uniqidhex}`);
            try {
                const cacheData = JSON.parse(fs.readFileSync(cacheMetaFile, 'utf8'));
                serveFromCache(cacheData, cacheContentFile, cacheMetaFile, res);
                return;
            } catch (parseError) {
                console.error(`Error parsing stale meta file ${cacheMetaFile}:`, parseError);
                // Fall through to generic error if stale cache is also broken
            }
        }
    }
    sendErrorResponse(res, HTTP_STATUS.BAD_GATEWAY, errorMessage || 'Bad Gateway');
}

async function handleMainRequest(req, res) {
    req.url = req.url.replace(/\/{2,}/g, '/');
    const parsedUrl = url.parse(req.url, true);
    const sign = parsedUrl.query.sign || '';
    let reqPath = parsedUrl.pathname.split('/')[1] || ''; // Ensure reqPath is not undefined
    let token = parsedUrl.pathname.split('/').slice(2).join('/');

    if (reqPath === 'favicon.ico') return handleFavicon(req, res);
    if (reqPath === 'endpoint') return handleEndpoint(req, res, parsedUrl);

    if (!token && reqPath) { // If token is empty but reqPath is not, assume reqPath is the token
        token = reqPath;
        reqPath = 'app'; // Default to 'app' if only one path segment is provided
    }

    const ALLOWED_PATHS = ['avatar', 'go', 'bbs', 'www', 'url', 'thumb', 'app'];
    if (!ALLOWED_PATHS.includes(reqPath) || !token) {
        return sendErrorResponse(res, HTTP_STATUS.BAD_REQUEST, `Bad Request: Invalid path or missing token.`);
    }

    viewsInfo.increment('request');
    const uniqidhex = crypto.createHash('md5').update(reqPath + token + sign).digest('hex');
    let cacheMetaFile = '';
    let cacheContentFile = '';

    if (pathIndex[uniqidhex]) {
        cacheMetaFile = pathModule.join(cacheDir, `${pathIndex[uniqidhex].uniqid}.meta`);
        cacheContentFile = pathModule.join(cacheDir, `${pathIndex[uniqidhex].uniqid}.content`);
    }

    if (pathIndex[uniqidhex] && isCacheValid(cacheMetaFile, cacheContentFile)) {
        const { cacheData, isNotModified } = await checkCacheHeaders(req, cacheMetaFile);
        if (isNotModified) {
            res.writeHead(HTTP_STATUS.NOT_MODIFIED);
            res.end();
        } else {
            viewsInfo.increment('cacheHit');
            serveFromCache(cacheData, cacheContentFile, cacheMetaFile, res);
        }
    } else {
        try {
            viewsInfo.increment('apiCall');
            const apiData = await fetchApiData(reqPath, token, sign);

            if (apiData.code === HTTP_STATUS.REDIRECT || apiData.code === 301) {
                return handleApiRedirect(res, apiData);
            }

            if (apiData.code === HTTP_STATUS.OK && apiData.data && apiData.data.url) {
                await processSuccessfulApiData(apiData, uniqidhex, reqPath, token, sign, res);
            } else {
                viewsInfo.increment('fetchApiWarning');
                await tryServeFromStaleCacheOrError(uniqidhex, res, apiData.message);
            }
        } catch (error) {
            viewsInfo.increment('fetchApiError');
            console.error('Error in API call or processing:', error);
            await tryServeFromStaleCacheOrError(uniqidhex, res, `Bad Gateway: API request failed. ${error.message}`);
        }
    }
}

const server = http.createServer(handleMainRequest);

// 检查缓存头并返回是否为304
async function checkCacheHeaders(req, cacheMetaFile) {
    try {
        const metaContent = fs.readFileSync(cacheMetaFile, 'utf8');
        const cacheData = JSON.parse(metaContent);
        const ifNoneMatch = req.headers['if-none-match'];
        const ifModifiedSince = req.headers['if-modified-since'];

        // Check ETag first
        if (ifNoneMatch && cacheData.uniqid && ifNoneMatch === cacheData.uniqid) {
            return { cacheData, isNotModified: true };
        }

        // Check If-Modified-Since
        if (ifModifiedSince && cacheData.headers && cacheData.headers['last-modified']) {
            try {
                const lastModifiedDate = new Date(cacheData.headers['last-modified']);
                const ifModifiedSinceDate = new Date(ifModifiedSince);
                // The time resolution of an HTTP date is one second.
                // If If-Modified-Since is at least as new as Last-Modified, send 304.
                if (lastModifiedDate.getTime() <= ifModifiedSinceDate.getTime()) {
                    return { cacheData, isNotModified: true };
                }
            } catch (dateParseError) {
                console.warn(`Error parsing date for cache header check (${cacheMetaFile}):`, dateParseError);
                // Proceed as if not modified check failed if dates are invalid
            }
        }
        return { cacheData, isNotModified: false };
    } catch (error) {
        console.error(`Error reading or parsing cache meta file ${cacheMetaFile} in checkCacheHeaders:`, error);
        // If we can't read meta, assume cache is invalid or treat as not modified: false
        // Returning a dummy cacheData or null might be better depending on how caller handles it.
        // For now, let it propagate and potentially fail later if cacheData is expected.
        // Or, more safely, indicate cache is not valid / not modified is false.
        return { cacheData: null, isNotModified: false }; // Indicate failure to load cacheData
    }
}


// 检查缓存是否有效
function isCacheValid(cacheMetaFile, cacheContentFile) {
    if (!fs.existsSync(cacheMetaFile) || !fs.existsSync(cacheContentFile)) {
        return false;
    }
    try {
        const metaContent = fs.readFileSync(cacheMetaFile, 'utf8');
        const cacheData = JSON.parse(metaContent);
        // Ensure expiration is a number and in the future
        return typeof cacheData.expiration === 'number' && cacheData.expiration > Date.now();
    } catch (error) {
        console.warn(`Error reading or parsing cache meta file ${cacheMetaFile} for validation:`, error);
        return false; // If meta file is corrupt or unreadable, cache is not valid
    }
}


// 从 API 获取数据
const API_TIMEOUT_MS = 5000;
const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36';

async function fetchApiData(reqPath, token, sign) {
    const queryParams = querystring.stringify({
        type: reqPath,
        sign: sign
    });
    const apiUrl = `${apiEndpoint}?${queryParams}`;
    const parsedApiUrl = new URL(apiUrl);
    const protocol = parsedApiUrl.protocol === 'https:' ? https : http;

    const options = {
        method: 'GET',
        headers: {
            'Accept': 'application/json; charset=utf-8',
            'User-Agent': USER_AGENT,
            'token': token
        },
        timeout: API_TIMEOUT_MS,
        rejectUnauthorized: false, // Allow self-signed certificates, use with caution
    };

    return new Promise((resolve, reject) => {
        const apiReq = protocol.request(apiUrl, options, (apiRes) => {
            let responseData = '';
            apiRes.setEncoding('utf8');
            apiRes.on('data', chunk => responseData += chunk);
            apiRes.on('end', () => {
                try {
                    if (apiRes.statusCode >= 400) {
                        // Treat HTTP errors from API as rejections for easier handling
                        console.error(`API request to ${apiUrl} failed with status ${apiRes.statusCode}: ${responseData}`);
                        // Attempt to parse for a message, but prioritize status code for error type
                        let errorPayload = { code: apiRes.statusCode, message: `API Error: ${apiRes.statusCode}` };
                        try {
                            const parsedError = JSON.parse(responseData);
                            if (parsedError && parsedError.message) errorPayload.message = parsedError.message;
                        } catch (e) { /* Ignore if response is not JSON */ }
                        resolve(errorPayload); // Resolve with error structure for consistency
                        return;
                    }
                    resolve(JSON.parse(responseData));
                } catch (parseError) {
                    console.error(`Error parsing JSON response from ${apiUrl}:`, parseError, responseData);
                    reject(new Error(`Failed to parse API response: ${parseError.message}`));
                }
            });
        });

        apiReq.on('timeout', () => {
            apiReq.destroy(); // Destroy the request to free up resources
            console.error(`API request to ${apiUrl} timed out after ${API_TIMEOUT_MS}ms`);
            reject(new Error('API request timed out'));
        });

        apiReq.on('error', (networkError) => {
            console.error(`API request to ${apiUrl} failed:`, networkError);
            reject(networkError);
        });

        apiReq.end();
    });
}

// createThumbnail
function createThumbnail(data, cacheContentFile, thumbCacheFile) {
    const { path, thumb } = data;
    const isVideo = path && typeof path === 'string' && path.includes('.mp4');
    if (isVideo || !thumb) return;
    if (fs.existsSync(thumbCacheFile)) return;
    const width = thumb.width;
    const height = thumb.height;
    sharp(cacheContentFile)
        .resize(width, height)
        .toFile(thumbCacheFile, (err, info) => {
            if (err) {
                console.error(`Error creating thumbnail for ${cacheContentFile}:`, err);
                return;
            }
        });

}


// 从真实 URL 获取数据并写入缓存
const REAL_URL_FETCH_TIMEOUT_MS = 0; // 0 means no timeout for the actual file download

const fetchAndServe = (data, tempCacheContentFile, cacheContentFile, cacheMetaFile, res) => {
    const protocol = data.realUrl.startsWith('https:') ? https : http;

    protocol.get(data.realUrl, { timeout: REAL_URL_FETCH_TIMEOUT_MS, rejectUnauthorized: false }, (realRes) => {
        const cacheStream = fs.createWriteStream(tempCacheContentFile, { flags: 'w' });

        let isVideo = data.path && typeof data.path === 'string' && data.path.includes('.mp4');
        // 确保 content-length 是有效的
        const contentLength = realRes.headers['content-length'];
        if (contentLength) {

            // contentLength 小于 2KB 且与缓存文件大小不一致时,重新获取
            if (contentLength < 2048 && data.headers['content-length'] !== contentLength) {
                console.warn('Warning: content-length is different for the response from:', data.realUrl);
                sendErrorResponse(res, HTTP_STATUS.BAD_GATEWAY, `Bad Gateway: Content-Length mismatch for ${data.realUrl}`);
                // Clean up temp file if stream hasn't started or failed early
                if (fs.existsSync(tempCacheContentFile)) {
                    fs.unlinkSync(tempCacheContentFile);
                }
                return;
            }

            data.headers['content-length'] = contentLength;
            // 更新 data 到缓存 cacheMetaFile
            fs.writeFileSync(cacheMetaFile, JSON.stringify(data));
        } else {
            console.warn('Warning: content-length is undefined for the response from:', data.realUrl);
        }

        const baseHeaders = {
            'Cloud-Type': data.cloudtype,
            'Cloud-Expiration': data.expiration,
            'ETag': data.uniqid || '',
            'Cache-Control': 'public, max-age=31536000', // 1 year
            'Expires': new Date(Date.now() + 31536000000).toUTCString(),
            'Accept-Ranges': 'bytes',
            'Connection': 'keep-alive',
            'Date': new Date().toUTCString(), // Should be set by the server, but good for consistency
            'Last-Modified': data.headers['last-modified'] || new Date(fs.statSync(cacheMetaFile).mtime).toUTCString(), // Prefer API's Last-Modified if available
        };
        const responseHeaders = {
            ...baseHeaders,
            'Content-Type': realRes.headers['content-type'] || (isVideo ? 'video/mp4' : 'application/octet-stream'), // Prefer actual content-type
            ...data.headers, // Allow API to override some headers if necessary
        };

        res.writeHead(realRes.statusCode, responseHeaders);
        realRes.pipe(cacheStream);
        realRes.pipe(res);

        realRes.on('end', () => {
            cacheStream.end(() => { // Ensure stream is fully flushed before renaming
                if (fs.existsSync(tempCacheContentFile)) {
                    try {
                        // Ensure the target directory exists before renaming
                        const targetDir = pathModule.dirname(cacheContentFile);
                        if (!fs.existsSync(targetDir)) {
                            fs.mkdirSync(targetDir, { recursive: true });
                        }
                        fs.renameSync(tempCacheContentFile, cacheContentFile);
                        console.log(`Successfully cached: ${cacheContentFile}`);


                        if (data.thumb) {
                            const thumbCacheFile = pathModule.join(cacheDir, `${data.uniqid}_thumb.jpg`);
                            if (!fs.existsSync(thumbCacheFile)) {
                                // 创建缩略图
                                createThumbnail(data, cacheContentFile, thumbCacheFile);
                            }
                        }
                    } catch (renameError) {
                        console.error(`Error renaming temp cache file ${tempCacheContentFile} to ${cacheContentFile}:`, renameError);
                        // If rename fails, try to remove the temp file to avoid clutter
                        try { fs.unlinkSync(tempCacheContentFile); } catch (e) { /* ignore */ }
                    }
                } else {
                    // This case might indicate an issue if the stream ended but no temp file was created/found
                    console.warn(`Temp cache file ${tempCacheContentFile} not found after stream end for ${data.realUrl}`);
                }
            });
        });

        realRes.on('error', (streamError) => {
            console.error(`Error during response stream from ${data.realUrl}:`, streamError);
            cacheStream.end(); // Close the writable stream
            handleResponseError(res, tempCacheContentFile, data.realUrl); // tempCacheContentFile might be partially written
        });

    }).on('error', (requestError) => {
        console.error(`Error making GET request to ${data.realUrl}:`, requestError);
        // No cacheStream involved here if the request itself fails before response
        handleResponseError(res, tempCacheContentFile, data.realUrl); // tempCacheContentFile might not exist or be empty
    });
};

// 从缓存中读取数据并返回
function serveFromCache(cacheData, cacheContentFile, cacheMetaFile, res) {
    if (!cacheData) { // Added check for null cacheData from checkCacheHeaders failure
        console.error(`serveFromCache called with null cacheData for ${cacheContentFile}`);
        sendErrorResponse(res, HTTP_STATUS.INTERNAL_SERVER_ERROR, 'Cache metadata unavailable.');
        return;
    }

    const baseHeaders = {
        'Cloud-Type': cacheData.cloudtype || 'unknown',
        'Cloud-Expiration': cacheData.expiration || 'N/A',
        'ETag': cacheData.uniqid || crypto.createHash('md5').update(fs.readFileSync(cacheContentFile)).digest('hex'), // Fallback ETag if missing
        'Cache-Control': 'public, max-age=31536000', // 1 year
        'Expires': new Date(Date.now() + 31536000000).toUTCString(),
        'Accept-Ranges': 'bytes',
        'Connection': 'keep-alive',
        'Date': new Date().toUTCString(),
        'Last-Modified': (cacheData.headers && cacheData.headers['last-modified']) || new Date(fs.statSync(cacheMetaFile).mtime).toUTCString(),
    };
    if (cacheData.thumb) {
        const thumbCacheFile = pathModule.join(cacheDir, `${cacheData.uniqid}_thumb.jpg`);
        if (fs.existsSync(thumbCacheFile)) {
            cacheData.headers['content-length'] = fs.statSync(thumbCacheFile).size;
            const responseHeaders = {
                ...baseHeaders,
                ...(cacheData.headers || {}),
                'ETag': (cacheData.uniqid || '') + '_thumb',
                'Content-Type': 'image/jpeg',
            };
            res.writeHead(HTTP_STATUS.OK, responseHeaders);
            const thumbStream = fs.createReadStream(thumbCacheFile);
            thumbStream.pipe(res);
            return;
        } else {
            createThumbnail(cacheData, cacheContentFile, thumbCacheFile)
        }
    }

    viewsInfo.increment('cacheCall');
    const readStream = fs.createReadStream(cacheContentFile);
    const isVideo = cacheData.path && typeof cacheData.path === 'string' && cacheData.path.includes('.mp4');

    let currentContentLength = cacheData.headers && cacheData.headers['content-length'] ? parseInt(cacheData.headers['content-length'], 10) : 0;

    if (!currentContentLength || currentContentLength === 0) {
        try {
            const stats = fs.statSync(cacheContentFile);
            currentContentLength = stats.size;
            if (currentContentLength > 0) {
                if (!cacheData.headers) cacheData.headers = {};
                cacheData.headers['content-length'] = currentContentLength.toString();
                // Update meta file if content-length was missing or zero
                fs.writeFileSync(cacheMetaFile, JSON.stringify(cacheData));
                console.log(`Updated content-length in ${cacheMetaFile} to ${currentContentLength}`);
            } else {
                console.warn(`Cached content file ${cacheContentFile} has size 0 or stat failed.`);
                // Potentially treat as an error or serve as is if 0 length is valid for some files
            }
        } catch (statError) {
            console.error(`Error stating cache content file ${cacheContentFile}:`, statError);
            handleCacheReadError(res, cacheContentFile); // Treat stat error as read error
            return;
        }
    }

    readStream.on('open', () => {

        const responseHeaders = {
            ...baseHeaders,
            'Content-Type': (cacheData.headers && cacheData.headers['content-type']) || (isVideo ? 'video/mp4' : 'application/octet-stream'),
            // Merge other headers from cacheData.headers, letting them override base if necessary
            // but ensure our critical headers like Content-Length (if updated) are preserved.
            ...(cacheData.headers || {}),
        };

        res.writeHead(HTTP_STATUS.OK, responseHeaders);
        readStream.pipe(res);
    });

    readStream.on('error', (err) => {
        console.error(`Read stream error for ${cacheContentFile}:`, err);
        handleCacheReadError(res, cacheContentFile);
    });

    // Handle cases where client closes connection prematurely
    res.on('close', () => {
        if (!res.writableEnded) {
            console.log(`Client closed connection prematurely for ${cacheContentFile}. Destroying read stream.`);
            readStream.destroy();
        }
    });
}


// 处理响应错误
const handleResponseError = (res, tempCacheContentFile, realUrl) => {
    viewsInfo.increment('fetchApiError');
    console.error(`Error fetching from real URL: ${realUrl}`);
    sendErrorResponse(res, HTTP_STATUS.BAD_GATEWAY, `Bad Gateway: Failed to fetch from ${realUrl}`);
    if (fs.existsSync(tempCacheContentFile)) {
        try {
            fs.unlinkSync(tempCacheContentFile);
        } catch (unlinkErr) {
            console.error(`Error unlinking temp file ${tempCacheContentFile}:`, unlinkErr);
        }
    }
};

// 处理缓存读取错误
const handleCacheReadError = (res, filePath) => {
    viewsInfo.increment('cacheReadError');
    console.error(`Error reading cache file: ${filePath}`);
    sendErrorResponse(res, HTTP_STATUS.INTERNAL_SERVER_ERROR, 'Internal Server Error: Unable to read cache content file');
};

// 启动服务器
server.listen(port, () => {
    console.log(`Proxy server is running on http://localhost:${port}`);
});

// 处理 SIGINT 信号(Ctrl+C)
process.on('SIGINT', () => {
    console.log('Received SIGINT. Shutting down gracefully...');
    server.close(() => {
        console.log('Server closed.');
        process.exit(0);
    });

    setTimeout(() => {
        console.error('Forcing shutdown...');
        process.exit(1);
    }, 10000);
});