import fs from 'node:fs';
import path from 'node:path';

const PUBLIC_ROOT = path.resolve('./public');
const TYPES = new Map([
  ['.html', 'text/html; charset=utf-8'],
  ['.css', 'text/css; charset=utf-8'],
  ['.js', 'text/javascript; charset=utf-8'],
  ['.txt', 'text/plain; charset=utf-8'],
  ['.png', 'image/png'],
  ['.jpg', 'image/jpeg'],
  ['.jpeg', 'image/jpeg'],
  ['.webp', 'image/webp'],
  ['.svg', 'image/svg+xml'],
  ['.zip', 'application/zip'],
  ['.ico', 'image/x-icon']
]);

export function withSecurityHeaders(res, options = {}) {
  res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
  res.setHeader('Referrer-Policy', 'same-origin');
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('X-Frame-Options', 'DENY');
  res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
  if (options.contentSecurityPolicy === false) return;

  const scriptSrc = options.scriptNonce ? `script-src 'self' 'nonce-${options.scriptNonce}'; ` : '';
  res.setHeader(
    'Content-Security-Policy',
    `default-src 'self'; ${scriptSrc}style-src 'self' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; img-src 'self' blob:; connect-src 'self'; object-src 'none'; base-uri 'none'; frame-ancestors 'none'; form-action 'self'`
  );
}

export function sendJson(res, statusCode, payload) {
  const body = Buffer.from(JSON.stringify(payload));
  res.writeHead(statusCode, {
    'Content-Type': 'application/json; charset=utf-8',
    'Content-Length': String(body.length)
  });
  endBody(res, body);
}

export function sendText(res, statusCode, message, contentType = 'text/plain; charset=utf-8') {
  const body = Buffer.from(message);
  res.writeHead(statusCode, {
    'Content-Type': contentType,
    'Content-Length': String(body.length)
  });
  endBody(res, body);
}

export function sendHtml(res, statusCode, html) {
  const body = Buffer.from(html);
  res.writeHead(statusCode, {
    'Content-Type': 'text/html; charset=utf-8',
    'Content-Length': String(body.length)
  });
  endBody(res, body);
}

export function sendFile(res, filePath, options = {}) {
  const resolved = options.absolute ? path.resolve(filePath) : path.resolve(filePath);
  if (!options.absolute && !resolved.startsWith(PUBLIC_ROOT + path.sep) && resolved !== path.join(PUBLIC_ROOT, 'index.html')) {
    return sendText(res, 403, 'Forbidden');
  }

  fs.stat(resolved, (statError, stats) => {
    if (statError || !stats.isFile()) {
      return sendText(res, 404, 'Not found');
    }

    if (!res.getHeader('Content-Type')) {
      res.setHeader('Content-Type', TYPES.get(path.extname(resolved).toLowerCase()) || 'application/octet-stream');
    }
    if (!res.getHeader('Content-Length')) {
      res.setHeader('Content-Length', String(stats.size));
    }
    if (res.req?.method === 'HEAD') {
      return res.end();
    }
    fs.createReadStream(resolved)
      .on('error', () => sendText(res, 500, 'File read failed'))
      .pipe(res);
  });
}

function endBody(res, body) {
  return res.req?.method === 'HEAD' ? res.end() : res.end(body);
}
