import { fal } from "@fal-ai/client";

const VIDEO_ENDPOINT = "fal-ai/kling-video/v3/standard/text-to-video" as const;
const allowedRatios = new Set(["16:9", "9:16", "1:1"]);

const styleDirections = {
  Cinematic:
    "Cinematic live-action film, grounded production design, natural skin and materials, expressive lighting, controlled depth of field, confident camera movement, polished color grade.",
  Anime:
    "Premium cinematic anime, expressive character acting, clean linework, richly painted backgrounds, layered atmospheric motion, dynamic but coherent camera movement.",
  Storybook:
    "Handcrafted illustrated storybook brought to life, tactile paper and painted textures, warm whimsical lighting, gentle character motion, elegant parallax depth.",
  Dreamlike:
    "Dreamlike surreal cinema, poetic visual transitions, luminous atmosphere, graceful slow motion, impossible but coherent spaces, ethereal light and color.",
} as const;

type StyleName = keyof typeof styleDirections;

function errorResponse(message: string, status: number) {
  return Response.json({ error: message }, { status });
}

export async function POST(request: Request) {
  if (!process.env.FAL_KEY) {
    return errorResponse("Video generation is not configured yet.", 503);
  }

  try {
    const body = (await request.json()) as {
      prompt?: unknown;
      style?: unknown;
      ratio?: unknown;
    };
    const prompt = typeof body.prompt === "string" ? body.prompt.trim() : "";
    const style = typeof body.style === "string" ? body.style : "";
    const ratio = typeof body.ratio === "string" ? body.ratio : "";

    if (!prompt || prompt.length > 2500) {
      return errorResponse("Enter a description between 1 and 2500 characters.", 400);
    }
    if (!(style in styleDirections) || !allowedRatios.has(ratio)) {
      return errorResponse("Choose a supported style and aspect ratio.", 400);
    }

    const styledPrompt = [
      prompt,
      styleDirections[style as StyleName],
      "Create one continuous, visually coherent video. Preserve subject identity and scene continuity. No captions, logos, UI, or watermarks.",
    ]
      .join("\n\n")
      .slice(0, 2500);

    const job = await fal.queue.submit(VIDEO_ENDPOINT, {
      input: {
        prompt: styledPrompt,
        duration: "3",
        aspect_ratio: ratio as "16:9" | "9:16" | "1:1",
        generate_audio: true,
        negative_prompt:
          "blur, distortion, low quality, morphing, duplicate subjects, broken anatomy, flicker, text, logo, watermark",
        cfg_scale: 0.5,
      },
    });

    return Response.json({ requestId: job.request_id }, { status: 202 });
  } catch (error) {
    if (error instanceof SyntaxError) {
      return errorResponse("Send a valid JSON request body.", 400);
    }
    console.error("Could not submit video generation:", error);
    return errorResponse("We could not start this video. Please try again.", 502);
  }
}

export async function GET(request: Request) {
  if (!process.env.FAL_KEY) {
    return errorResponse("Video generation is not configured yet.", 503);
  }

  const requestId = new URL(request.url).searchParams.get("requestId") ?? "";
  if (!/^[a-zA-Z0-9_-]{8,200}$/.test(requestId)) {
    return errorResponse("A valid video request ID is required.", 400);
  }

  try {
    const status = await fal.queue.status(VIDEO_ENDPOINT, { requestId });
    if (status.status !== "COMPLETED") {
      return Response.json({ status: status.status });
    }

    const result = await fal.queue.result(VIDEO_ENDPOINT, { requestId });
    const videoUrl = result.data.video?.url;
    if (!videoUrl) {
      return errorResponse("The render completed without a video file.", 502);
    }

    return Response.json({ status: "COMPLETED", videoUrl });
  } catch (error) {
    console.error("Could not retrieve video generation:", error);
    return errorResponse("We could not retrieve this video. Please try again.", 502);
  }
}
