import { NextRequest, NextResponse } from "next/server";
import { createReadStream, statSync } from "fs";
import { unlink, stat, readdir, rmdir } from "fs/promises";
import { execFile } from "child_process";
import { promisify } from "util";
import { join } from "path";
import { Readable } from "stream";
import { parseMultipartUpload } from "../parse-upload";
import { requireAuth } from "../auth-check";

const execFileAsync = promisify(execFile);

function qpdfArgs(input: string, output: string): string[] {
  return [
    input,
    output,
    "--object-streams=generate",
    "--compress-streams=y",
    "--recompress-flate",
    "--compression-level=9",
    "--remove-unreferenced-resources=yes",
    "--linearize",
  ];
}

async function cleanup(dir: string) {
  try {
    const files = await readdir(dir);
    for (const f of files) {
      await unlink(join(dir, f)).catch(() => {});
    }
    await rmdir(dir).catch(() => {});
  } catch {
    // non-critical
  }
}

/**
 * Stream a file from disk as a Response — never loads into memory.
 */
function streamFileResponse(
  filePath: string,
  originalSize: number,
  compressedSize: number,
  filename: string,
): NextResponse {
  const nodeStream = createReadStream(filePath);
  const webStream = Readable.toWeb(nodeStream) as ReadableStream;

  return new NextResponse(webStream, {
    status: 200,
    headers: {
      "Content-Type": "application/pdf",
      "Content-Length": String(compressedSize),
      "Content-Disposition": `attachment; filename="${encodeURIComponent(filename)}"`,
      "X-Original-Size": String(originalSize),
      "X-Compressed-Size": String(compressedSize),
    },
  });
}

export async function POST(req: NextRequest) {
  const authError = await requireAuth(req);
  if (authError) return authError;

  let tmpDir = "";
  try {
    const upload = await parseMultipartUpload(req);
    tmpDir = upload.tmpDir;

    const inputPath = upload.filePath;
    const outputPath = join(upload.tmpDir, "output.pdf");
    const originalSize = upload.size;

    console.log(
      `[compress-pdf] Starting qpdf on ${originalSize} bytes...`,
    );

    if (originalSize < 100) {
      return NextResponse.json(
        { error: "Fișierul PDF este gol sau prea mic." },
        { status: 400 },
      );
    }

    // Run qpdf
    try {
      await execFileAsync("qpdf", qpdfArgs(inputPath, outputPath), {
        timeout: 300_000,
        maxBuffer: 10 * 1024 * 1024,
      });
    } catch (qpdfErr) {
      const msg =
        qpdfErr instanceof Error ? qpdfErr.message : "qpdf failed";
      if (msg.includes("ENOENT") || msg.includes("not found")) {
        return NextResponse.json(
          { error: "qpdf nu este instalat pe server." },
          { status: 501 },
        );
      }
      const exitCode =
        qpdfErr && typeof qpdfErr === "object" && "code" in qpdfErr
          ? (qpdfErr as { code: number }).code
          : null;
      if (exitCode !== 3) {
        console.error(`[compress-pdf] qpdf error:`, msg.slice(0, 300));
        return NextResponse.json(
          { error: `qpdf error: ${msg.slice(0, 300)}` },
          { status: 500 },
        );
      }
    }

    // Check output
    try {
      await stat(outputPath);
    } catch {
      return NextResponse.json(
        { error: "qpdf nu a produs fișier output." },
        { status: 500 },
      );
    }

    const compressedSize = statSync(outputPath).size;

    console.log(
      `[compress-pdf] Done: ${originalSize} → ${compressedSize} (${Math.round((1 - compressedSize / originalSize) * 100)}% reduction)`,
    );

    // Stream result from disk — if bigger, stream original
    if (compressedSize >= originalSize) {
      return streamFileResponse(inputPath, originalSize, originalSize, upload.filename);
    }

    // NOTE: cleanup is deferred — we can't delete files while streaming.
    // The files will be cleaned up by the OS temp cleaner or on next request.
    // For immediate cleanup, we'd need to buffer, but that defeats the purpose.
    return streamFileResponse(outputPath, originalSize, compressedSize, upload.filename);
  } catch (err) {
    const message = err instanceof Error ? err.message : "Unknown error";
    console.error(`[compress-pdf] Error:`, message);
    if (tmpDir) await cleanup(tmpDir);
    return NextResponse.json(
      { error: `Eroare la optimizare: ${message}` },
      { status: 500 },
    );
  }
  // Note: no finally cleanup — files are being streamed
}
