import { createServer } from 'node:http';
import { createReadStream, promises as fs } from 'node:fs';
import os from 'node:os';
import path from 'node:path';
const DEFAULT_STORAGE_DIR = path.join(os.homedir(), '.mcp-desktop', 'files');
let serverPromise;
export const ensureLocalFileServer = (options = {}) => {
    if (!serverPromise) {
        serverPromise = startServer(options);
    }
    return serverPromise;
};
const startServer = async (options) => {
    const storageDir = path.resolve(options.storageDir ?? DEFAULT_STORAGE_DIR);
    await fs.mkdir(storageDir, { recursive: true });
    const host = options.host ?? '127.0.0.1';
    const port = options.port ?? 0;
    let resolvedBaseUrl = '';
    const server = createServer(async (req, res) => {
        setCorsHeaders(res);
        if (req.method === 'OPTIONS') {
            res.writeHead(204);
            res.end();
            return;
        }
        try {
            const url = new URL(req.url ?? '/', `http://${req.headers.host ?? `${host}:${port}`}`);
            if (req.method === 'POST' && url.pathname === '/files') {
                await handleFileUpload(req, res, storageDir, resolvedBaseUrl);
                return;
            }
            if (req.method === 'GET' && url.pathname.startsWith('/files/')) {
                await handleFileDownload(url.pathname, res, storageDir);
                return;
            }
            sendJson(res, 404, { error: 'Not found' });
        }
        catch (error) {
            sendJson(res, 500, { error: error.message });
        }
    });
    await new Promise((resolve, reject) => {
        server.once('error', reject);
        server.listen(port, host, () => resolve());
    });
    const address = server.address();
    if (!address || typeof address === 'string') {
        server.close();
        throw new Error('Failed to determine local file server address');
    }
    resolvedBaseUrl = `http://${address.address === '::' ? '127.0.0.1' : address.address}:${address.port}`;
    return {
        baseUrl: resolvedBaseUrl,
        host: address.address,
        port: address.port
    };
};
const handleFileUpload = async (req, res, storageDir, baseUrl) => {
    try {
        const payload = await parseRequestBody(req);
        validatePayload(payload);
        const fileId = sanitizeComponent(payload.id);
        const fileName = sanitizeFileName(payload.name);
        const buffer = decodeDataUrl(payload.dataUrl);
        if (payload.size && payload.size !== buffer.length) {
            throw new Error('Uploaded file size does not match declared size');
        }
        const fileDir = path.join(storageDir, fileId);
        await fs.mkdir(fileDir, { recursive: true });
        const filePath = safeJoin(fileDir, fileName);
        await fs.writeFile(filePath, buffer);
        const metadata = {
            mimeType: payload.mimeType,
            originalName: payload.name,
            size: buffer.length,
            uploadedAt: new Date().toISOString()
        };
        await fs.writeFile(`${filePath}.meta.json`, JSON.stringify(metadata, null, 2));
        const relativePath = `/files/${encodeURIComponent(fileId)}/${encodeURIComponent(fileName)}`;
        const sourceUrl = `${baseUrl}${relativePath}`;
        sendJson(res, 201, { sourceUrl });
    }
    catch (error) {
        sendJson(res, 400, { error: error.message });
    }
};
const handleFileDownload = async (pathname, res, storageDir) => {
    const segments = pathname.split('/').filter(Boolean);
    if (segments.length < 3) {
        sendJson(res, 400, { error: 'Invalid file path' });
        return;
    }
    const [, fileIdEncoded, ...filenameParts] = segments;
    const fileId = sanitizeComponent(decodeURIComponent(fileIdEncoded));
    const fileName = sanitizeFileName(decodeURIComponent(filenameParts.join('/')));
    const fileDir = path.join(storageDir, fileId);
    const filePath = safeJoin(fileDir, fileName);
    const metadataPath = `${filePath}.meta.json`;
    try {
        await fs.access(filePath);
    }
    catch {
        sendJson(res, 404, { error: 'File not found' });
        return;
    }
    let mimeType = 'application/octet-stream';
    try {
        const metadataRaw = await fs.readFile(metadataPath, 'utf-8');
        const metadata = JSON.parse(metadataRaw);
        if (metadata.mimeType) {
            mimeType = metadata.mimeType;
        }
    }
    catch {
        // ignore metadata errors and fall back to default mime type
    }
    res.writeHead(200, {
        'Content-Type': mimeType,
        'Cache-Control': 'public, max-age=31536000, immutable'
    });
    const stream = createReadStream(filePath);
    stream.on('error', () => {
        if (!res.headersSent) {
            sendJson(res, 500, { error: 'Error reading file' });
        }
        else {
            res.destroy();
        }
    });
    stream.pipe(res);
};
const parseRequestBody = async (req) => {
    const chunks = [];
    for await (const chunk of req) {
        chunks.push(typeof chunk === 'string' ? Buffer.from(chunk, 'utf-8') : chunk);
    }
    const body = Buffer.concat(chunks).toString('utf-8');
    try {
        return JSON.parse(body);
    }
    catch {
        throw new Error('Invalid JSON payload');
    }
};
const validatePayload = (payload) => {
    if (!payload || typeof payload !== 'object') {
        throw new Error('Invalid payload');
    }
    const requiredFields = ['id', 'name', 'mimeType', 'size', 'dataUrl'];
    for (const field of requiredFields) {
        if (!payload[field]) {
            throw new Error(`Missing required field: ${field}`);
        }
    }
    if (!/^data:.*;base64,/.test(payload.dataUrl)) {
        throw new Error('dataUrl must be a base64-encoded data URI');
    }
};
const decodeDataUrl = (dataUrl) => {
    const [, base64Data] = dataUrl.split(',');
    if (!base64Data) {
        throw new Error('Invalid dataUrl');
    }
    return Buffer.from(base64Data, 'base64');
};
const sanitizeComponent = (value) => {
    return value.replace(/[^a-zA-Z0-9-_]/g, '_');
};
const sanitizeFileName = (value) => {
    const normalized = value.replace(/\\/g, '/').split('/').pop() ?? 'file';
    return normalized.replace(/[^a-zA-Z0-9._-]/g, '_');
};
const safeJoin = (base, target) => {
    const resolved = path.resolve(base, target);
    if (!resolved.startsWith(base)) {
        throw new Error('Invalid file path');
    }
    return resolved;
};
const setCorsHeaders = (res) => {
    res.setHeader('Access-Control-Allow-Origin', '*');
    res.setHeader('Access-Control-Allow-Methods', 'GET,POST,OPTIONS');
    res.setHeader('Access-Control-Allow-Headers', 'Content-Type,Authorization');
};
const sendJson = (res, status, payload) => {
    if (!res.headersSent) {
        res.writeHead(status, { 'Content-Type': 'application/json' });
    }
    res.end(JSON.stringify(payload));
};
export const __resetLocalFileServerForTests = () => {
    serverPromise = undefined;
};
