/**
 * GET /api/scratch/<path...>
 *   ?download=1   → force Content-Disposition: attachment
 *
 * Serves files from `<dataDir>/scratch/` so chat-emitted paths like
 * `scratch/report-2026-06-08.md` can be clicked open from the chat UI.
 * Path-traversal protected: any segment containing `..` or any resolved
 * path escaping the scratch root is rejected.
 *
 * For files outside scratch/ (e.g. `tmp/foo.md` from save_tmp_file,
 * `flows/x.yaml`) the dataDir-wide route at /api/files/[...path] is the
 * one to use — it shares the same MIME / download / size guards but
 * roots at <dataDir>/ with a sensitive-file blacklist.
 *
 * Auth-gated by Forge's proxy middleware just like every other /api/*.
 */

import { NextResponse } from 'next/server';
import { existsSync, readFileSync, statSync } from 'node:fs';
import { join, resolve, extname } from 'node:path';
import { getDataDir } from '@/lib/dirs';

const MIME: Record<string, string> = {
  '.md': 'text/markdown; charset=utf-8',
  '.txt': 'text/plain; charset=utf-8',
  '.json': 'application/json; charset=utf-8',
  '.yaml': 'application/yaml; charset=utf-8',
  '.yml': 'application/yaml; charset=utf-8',
  '.csv': 'text/csv; charset=utf-8',
  '.log': 'text/plain; charset=utf-8',
  '.html': 'text/html; charset=utf-8',
  '.pdf': 'application/pdf',
  '.png': 'image/png',
  '.jpg': 'image/jpeg',
  '.jpeg': 'image/jpeg',
  '.gif': 'image/gif',
  '.svg': 'image/svg+xml',
};

export async function GET(
  req: Request,
  ctx: { params: Promise<{ path: string[] }> },
) {
  const { path: parts } = await ctx.params;
  if (!parts || parts.length === 0) {
    return NextResponse.json({ ok: false, error: 'missing path' }, { status: 400 });
  }
  // Reject explicit traversal attempts before path construction.
  for (const seg of parts) {
    if (seg === '..' || seg.startsWith('..')) {
      return NextResponse.json({ ok: false, error: 'path traversal rejected' }, { status: 400 });
    }
  }
  const scratchRoot = resolve(join(getDataDir(), 'scratch'));
  const target = resolve(join(scratchRoot, ...parts));
  // Verify the resolved path stays under scratchRoot — handles symlink
  // tricks the segment check above can't catch.
  if (!target.startsWith(scratchRoot + '/') && target !== scratchRoot) {
    return NextResponse.json({ ok: false, error: 'path traversal rejected' }, { status: 400 });
  }
  if (!existsSync(target)) {
    return NextResponse.json({ ok: false, error: 'not found' }, { status: 404 });
  }
  const st = statSync(target);
  if (!st.isFile()) {
    return NextResponse.json({ ok: false, error: 'not a file' }, { status: 400 });
  }
  const url = new URL(req.url);
  const download = url.searchParams.get('download') === '1';
  const ext = extname(target).toLowerCase();
  const mime = MIME[ext] || 'application/octet-stream';
  const data = readFileSync(target);
  const filename = parts[parts.length - 1];
  return new NextResponse(new Uint8Array(data), {
    status: 200,
    headers: {
      'Content-Type': mime,
      'Content-Length': String(st.size),
      'Content-Disposition': `${download ? 'attachment' : 'inline'}; filename="${filename.replace(/"/g, '\\"')}"`,
      'Cache-Control': 'private, max-age=0, must-revalidate',
    },
  });
}
