import path from "node:path";
import { bundle } from "@remotion/bundler";
import { renderMedia, renderStill, selectComposition } from "@remotion/renderer";
import { config } from "../config.js";
import {
  ColibriRemotionInputProps,
  manifestToInputProps,
  manifestSchema,
  resolveCompositionId,
  resolveThumbnailCompositionId,
} from "../schemas/manifest.js";
import { downloadManifestAssets, removeWorkDir } from "../storage/download-assets.js";
import { uploadRenderOutputs } from "../storage/r2-uploader.js";
import { LaravelClient } from "../api/laravel-client.js";

let bundled: string | null = null;

async function getServeUrl(): Promise<string> {
  if (bundled) {
    return bundled;
  }

  bundled = await bundle({
    entryPoint: path.resolve(config.remotion.entryPoint),
  });

  return bundled;
}

export async function processRenderJob(
  renderJobUuid: string,
  api: LaravelClient,
): Promise<void> {
  const workDir = path.resolve(config.tmpDir, renderJobUuid);

  try {
    await api.patchRenderJob(renderJobUuid, {
      status: "processing",
      progress_pct: 5,
      worker_id: config.workerId,
    });

    const rawManifest = await api.fetchManifest(renderJobUuid);
    const manifest = manifestSchema.parse(rawManifest);

    await api.patchRenderJob(renderJobUuid, { progress_pct: 15 });

    const downloads = await downloadManifestAssets(manifest, workDir);
    const inputProps = manifestToInputProps(manifest, downloads, config.remotion);

    await api.patchRenderJob(renderJobUuid, { progress_pct: 30 });

    const serveUrl = await getServeUrl();
    const compositionId = resolveCompositionId(manifest.template_slug);
    const thumbnailCompositionId = resolveThumbnailCompositionId(manifest.template_slug);

    const composition = await selectComposition({
      serveUrl,
      id: compositionId,
      inputProps: inputProps as ColibriRemotionInputProps,
    });

    const outputLocation = path.join(workDir, "output.mp4");
    const thumbnailLocation = path.join(workDir, "thumbnail.jpg");

    await renderMedia({
      serveUrl,
      composition,
      codec: "h264",
      outputLocation,
      inputProps: inputProps as ColibriRemotionInputProps,
      onProgress: ({ progress }) => {
        const pct = 30 + Math.round(progress * 55);
        void api.patchRenderJob(renderJobUuid, { progress_pct: pct }).catch(() => undefined);
      },
    });

    await renderStill({
      serveUrl,
      composition: await selectComposition({
        serveUrl,
        id: thumbnailCompositionId,
        inputProps: inputProps as ColibriRemotionInputProps,
      }),
      output: thumbnailLocation,
      inputProps: inputProps as ColibriRemotionInputProps,
      imageFormat: "jpeg",
    });

    await api.patchRenderJob(renderJobUuid, { progress_pct: 90 });

    const uploaded = await uploadRenderOutputs(
      manifest.generation_id,
      manifest.render_job_id,
      outputLocation,
      thumbnailLocation,
    );

    await api.patchRenderJob(renderJobUuid, {
      status: "ready",
      progress_pct: 100,
      output_path: uploaded.outputPath,
      thumbnail_path: uploaded.thumbnailPath,
      duration_seconds: Math.round(composition.durationInFrames / config.remotion.fps),
    });

    console.log(`[worker] Render complete: ${renderJobUuid} -> ${uploaded.outputPath}`);
  } catch (error) {
    const message = error instanceof Error ? error.message : "Unknown render failure";

    console.error(`[worker] Render failed: ${renderJobUuid}`, error);

    await api.patchRenderJob(renderJobUuid, {
      status: "failed",
      progress_pct: 100,
      worker_id: config.workerId,
      error_message: message.slice(0, 5000),
    });
  } finally {
    await removeWorkDir(workDir).catch(() => undefined);
  }
}
