import { NextRequest, NextResponse } from "next/server";
import * as fs from "fs";
import * as path from "path";
import * as os from "os";

const MERGE_DIR = path.join(os.tmpdir(), "yt-merge");

// UUID v4 format validation to prevent path traversal
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.mp4$/i;

/**
 * GET /api/download/serve?file=<uuid>.mp4&filename=<title>.mp4
 * Streams a server-side merged video file to the client.
 * Deletes the file after the response is complete.
 */
export async function GET(request: NextRequest): Promise<NextResponse> {
  const { searchParams } = new URL(request.url);
  const file = searchParams.get("file");
  let filename = searchParams.get("filename") || "download.mp4";

  if (!file || !UUID_REGEX.test(file)) {
    return NextResponse.json(
      { error: "Invalid or missing file parameter" },
      { status: 400 }
    );
  }

  const filePath = path.join(MERGE_DIR, file);

  // Ensure the resolved path is still inside MERGE_DIR (extra path traversal guard)
  if (!path.resolve(filePath).startsWith(path.resolve(MERGE_DIR))) {
    return NextResponse.json(
      { error: "Invalid file path" },
      { status: 400 }
    );
  }

  if (!fs.existsSync(filePath)) {
    return NextResponse.json(
      { error: "File not found or expired" },
      { status: 404 }
    );
  }

  try {
    const stat = fs.statSync(filePath);
    const fileSize = stat.size;

    // Encode filename for Content-Disposition
    const safeFilename = encodeURIComponent(filename)
      .replace(/['()]/g, escape)
      .replace(/\*/g, "%2A");

    // Create a ReadableStream from the file
    const fileStream = fs.createReadStream(filePath);
    const readable = new ReadableStream({
      start(controller) {
        fileStream.on("data", (chunk: Buffer | string) => {
          const buf = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
          controller.enqueue(new Uint8Array(buf));
        });
        fileStream.on("end", () => {
          controller.close();
          // Schedule file deletion after stream completes
          scheduleCleanup(filePath);
        });
        fileStream.on("error", (err) => {
          console.error("[SERVE] Stream error:", err);
          controller.error(err);
        });
      },
      cancel() {
        fileStream.destroy();
        scheduleCleanup(filePath);
      },
    });

    const headers = new Headers();
    headers.set("Content-Type", "video/mp4");
    headers.set("Content-Length", fileSize.toString());
    headers.set(
      "Content-Disposition",
      `attachment; filename*=UTF-8''${safeFilename}`
    );

    return new NextResponse(readable, {
      status: 200,
      headers,
    });
  } catch (error) {
    console.error("[SERVE] Error serving file:", error);
    return NextResponse.json(
      { error: "Failed to serve file" },
      { status: 500 }
    );
  }
}

/**
 * Deletes the file after a short delay to ensure the stream has fully flushed.
 */
function scheduleCleanup(filePath: string): void {
  setTimeout(() => {
    try {
      if (fs.existsSync(filePath)) {
        fs.unlinkSync(filePath);
        console.log("[SERVE] Cleaned up:", path.basename(filePath));
      }
    } catch (err) {
      console.error("[SERVE] Cleanup error:", err);
    }
  }, 5000); // 5 second delay
}
