import { type LoaderFunctionArgs } from "@remix-run/server-runtime";
import { json } from "@remix-run/node";
import { z } from "zod";
import { requireUserId } from "~/services/session.server";
import { EnvironmentParamSchema } from "~/utils/pathBuilder";
import { parsePeriodToMs } from "~/utils/periods";
import { findProjectBySlug } from "~/models/project.server";
import { findEnvironmentBySlug } from "~/models/runtimeEnvironment.server";
import { clickhouseFactory } from "~/services/clickhouse/clickhouseFactoryInstance.server";
import {
  PromptPresenter,
  type GenerationRow,
  type GenerationsPagination,
} from "~/presenters/v3/PromptPresenter.server";

export type { GenerationRow, GenerationsPagination };

export type GenerationsResponse = {
  generations: GenerationRow[];
  pagination: GenerationsPagination;
};

export const loader = async ({ request, params }: LoaderFunctionArgs) => {
  const userId = await requireUserId(request);
  const { projectParam, organizationSlug, envParam, promptSlug } = EnvironmentParamSchema.extend({
    promptSlug: z.string(),
  }).parse(params);

  const project = await findProjectBySlug(organizationSlug, projectParam, userId);
  if (!project) throw new Response("Project not found", { status: 404 });

  const environment = await findEnvironmentBySlug(project.id, envParam, userId);
  if (!environment) throw new Response("Environment not found", { status: 404 });

  const url = new URL(request.url);

  const versions = url.searchParams
    .getAll("versions")
    .map(Number)
    .filter((n) => Number.isInteger(n) && n > 0);

  const period = url.searchParams.get("period") ?? "7d";
  if (!/^\d+[mhdw]$/.test(period)) {
    return json({ generations: [], pagination: {} } satisfies GenerationsResponse);
  }

  const fromTime = url.searchParams.get("from");
  const toTime = url.searchParams.get("to");
  const cursorParam = url.searchParams.get("cursor") ?? undefined;

  const periodMs = parsePeriodToMs(period);
  const startTime = fromTime ? new Date(fromTime) : new Date(Date.now() - periodMs);
  const endTime = toTime ? new Date(toTime) : new Date();

  if (isNaN(startTime.getTime()) || isNaN(endTime.getTime())) {
    return json({ generations: [], pagination: {} } satisfies GenerationsResponse);
  }

  const models = url.searchParams.getAll("models").filter(Boolean);
  const operations = url.searchParams.getAll("operations").filter(Boolean);
  const providers = url.searchParams.getAll("providers").filter(Boolean);

  const clickhouse = await clickhouseFactory.getClickhouseForOrganization(
    project.organizationId,
    "standard"
  );
  const presenter = new PromptPresenter(clickhouse);
  const result = await presenter.listGenerations({
    environmentId: environment.id,
    promptSlug,
    promptVersions: versions.length > 0 ? versions : undefined,
    startTime,
    endTime,
    cursor: cursorParam,
    responseModels: models.length > 0 ? models : undefined,
    operations: operations.length > 0 ? operations : undefined,
    providers: providers.length > 0 ? providers : undefined,
  });

  return json(result satisfies GenerationsResponse);
};
