import type { ProjectStatus, ProjectType } from "@/lib/projects";

export interface ProjectDetail {
  id: string;
  name: string;
  type: ProjectType;
  scene_data: Record<string, unknown>;
  status: ProjectStatus;
  updated_at: string;
}

export interface ProjectApiResponse {
  project: ProjectDetail;
}

export class ProjectApiError extends Error {
  readonly status: number;

  constructor(message: string, status: number) {
    super(message);
    this.name = "ProjectApiError";
    this.status = status;
  }
}

export function isProjectApiError(error: unknown): error is ProjectApiError {
  return error instanceof ProjectApiError;
}

export function isProjectNotFoundError(error: unknown): boolean {
  return isProjectApiError(error) && error.status === 404;
}

async function parseProjectResponse(
  response: Response
): Promise<ProjectApiResponse & { error?: string }> {
  return (await response.json()) as ProjectApiResponse & { error?: string };
}

export async function fetchProject(
  projectId: string
): Promise<ProjectApiResponse> {
  const response = await fetch(`/api/projects/${projectId}`, {
    method: "GET",
    cache: "no-store",
  });

  const data = await parseProjectResponse(response);

  if (!response.ok) {
    throw new ProjectApiError(data.error ?? "Failed to load project", response.status);
  }

  return data;
}

export async function patchProjectSceneData(
  projectId: string,
  scene_data: Record<string, unknown>
): Promise<ProjectApiResponse> {
  const response = await fetch(`/api/projects/${projectId}`, {
    method: "PATCH",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ scene_data }),
  });

  const data = await parseProjectResponse(response);

  if (!response.ok) {
    throw new ProjectApiError(data.error ?? "Failed to save project", response.status);
  }

  return data;
}

export interface ContentValueUpdate {
  key: string;
  value: string;
  valueFileId?: string | null;
}

/**
 * Persist the user's per-input values to the saved project's content elements so the
 * render binder uses them. Best-effort (callers may ignore failures).
 */
export async function patchProjectContents(
  projectId: string,
  items: ContentValueUpdate[]
): Promise<void> {
  if (items.length === 0) return;
  const response = await fetch(`/api/projects/${projectId}/contents`, {
    method: "PATCH",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ items }),
  });
  if (!response.ok) {
    throw new ProjectApiError("Failed to save inputs", response.status);
  }
}

/** Extract editable input values from studio scene_data (bridged `c-<key>` layers). */
export function contentValuesFromSceneData(
  sceneData: Record<string, unknown>
): ContentValueUpdate[] {
  const scenes = Array.isArray(sceneData.scenes) ? sceneData.scenes : [];
  const items: ContentValueUpdate[] = [];
  const seen = new Set<string>();
  for (const s of scenes) {
    const layers = s && typeof s === "object" && Array.isArray((s as Record<string, unknown>).layers)
      ? ((s as Record<string, unknown>).layers as unknown[])
      : [];
    for (const l of layers) {
      if (!l || typeof l !== "object") continue;
      const layer = l as Record<string, unknown>;
      if (typeof layer.id !== "string" || !layer.id.startsWith("c-")) continue;
      const key = layer.id.slice(2);
      if (seen.has(key)) continue;
      seen.add(key);
      const props = (layer.props && typeof layer.props === "object" ? layer.props : {}) as Record<string, unknown>;
      const value =
        typeof props.text === "string" ? props.text
        : typeof props.src === "string" ? props.src
        : "";
      items.push({ key, value });
    }
  }
  return items;
}
