import type { Db } from "@paperclipai/db";
import { contentJobs } from "@paperclipai/db";
import { MAX_GENERATED_ASSET_BYTES } from "../attachment-types.js";
import type { StorageService } from "../storage/types.js";
import { contentJobStore } from "./content-job-store.js";
import { assetService } from "./assets.js";
import { publishLiveEvent } from "./live-events.js";
import type { RenderResult } from "./renderers/types.js";

type ContentJob = typeof contentJobs.$inferSelect;

export async function renderContent(
  jobType: string,
  input: Record<string, unknown>,
  companyId?: string,
  jobId?: string,
): Promise<RenderResult> {
  switch (jobType) {
    case "diagram": {
      const { renderDiagram } = await import("./renderers/diagram-renderer.js");
      return renderDiagram(input);
    }
    case "icon-set": {
      const { renderIconSet } = await import("./renderers/icon-renderer.js");
      return renderIconSet(input);
    }
    case "theme-palette": {
      const { renderThemePalette } = await import("./renderers/theme-renderer.js");
      return renderThemePalette(input);
    }
    case "wallpaper": {
      const { renderWallpaper } = await import("./renderers/wallpaper-renderer.js");
      return renderWallpaper(input);
    }
    case "social-post": {
      const { renderSocialPost } = await import("./renderers/social-renderer.js");
      return renderSocialPost(input);
    }
    case "convert": {
      const { renderConvert } = await import("./renderers/convert-renderer.js");
      return renderConvert(input);
    }
    case "pdf-document": {
      const { renderPdfDocument } = await import("./renderers/pdf-renderer.js");
      return renderPdfDocument(input);
    }
    case "brand-kit": {
      const { renderBrandKit } = await import("./renderers/brand-renderer.js");
      return renderBrandKit(input);
    }
    case "presentation": {
      const { renderPresentation } = await import("./renderers/presentation-renderer.js");
      return renderPresentation(input, companyId, jobId);
    }
    default:
      throw new Error(`Unknown jobType: ${jobType}`);
  }
}

async function runJob(db: Db, storage: StorageService, job: ContentJob): Promise<void> {
  const store = contentJobStore(db);

  await store.transition(job.id, { status: "running", startedAt: new Date() });
  publishLiveEvent({
    companyId: job.companyId,
    type: "content_job.running",
    payload: { jobId: job.id },
  });

  try {
    const result = await renderContent(job.jobType, job.input as Record<string, unknown>, job.companyId, job.id);

    if (result.buffer.byteLength > MAX_GENERATED_ASSET_BYTES) {
      throw new Error(
        `Generated asset size ${result.buffer.byteLength} exceeds limit of ${MAX_GENERATED_ASSET_BYTES}`,
      );
    }

    const stored = await storage.putFile({
      companyId: job.companyId,
      namespace: "generated",
      originalFilename: result.filename,
      contentType: result.contentType,
      body: result.buffer,
    });

    const asset = await assetService(db).create(job.companyId, {
      ...stored,
      sourceTaskId: job.sourceTaskId,
      createdByAgentId: null,
      createdByUserId: null,
    });

    await store.transition(job.id, {
      status: "done",
      resultAssetId: asset!.id,
      finishedAt: new Date(),
    });
    publishLiveEvent({
      companyId: job.companyId,
      type: "content_job.done",
      payload: { jobId: job.id, assetId: asset!.id },
    });
  } catch (err) {
    const errorMessage = err instanceof Error ? err.message : String(err);

    await store.transition(job.id, {
      status: "failed",
      errorMessage,
      finishedAt: new Date(),
    });
    publishLiveEvent({
      companyId: job.companyId,
      type: "content_job.failed",
      payload: { jobId: job.id, errorMessage },
    });
  }
}

export const contentJobRunner = {
  dispatch(db: Db, storage: StorageService, job: ContentJob): void {
    void runJob(db, storage, job);
  },
};
