import { getWarpEnabledAndPopulated } from '@suno/studiokit/warpUtils';
import type { FetchResponse } from 'openapi-fetch';

import { toast } from '@/components/toast/Toast';
import { ApiClient } from '@/lib/apiClient';
import type { paths } from '@/lib/gen';
import { Clip } from '@/state/clipStore';

import removeNonUploadedClips from '../actions/removeNonUploadedClips';
import resolveHeardState from '../resolveHeardState';
import { StudioProjectState } from '../types';

const renderStateCache: Record<
  string,
  Promise<
    FetchResponse<
      paths['/api/studio/render-state']['post'],
      'post',
      `${string}/${string}`
    >
  >
> = {};

export const getRenderStateKey = (
  state: StudioProjectState,
  startBeats: number,
  endBeats: number
) => {
  return removeNonUploadedClips(state)
    .tracks.flatMap((t) => {
      return [
        t.id,
        t.amplitude,
        t.balance,
        JSON.stringify(t.eq),
        ...t.clips.flatMap((c) => [
          c.id,
          c.startBeats,
          c.endBeats,
          c.readStartBeats,
          c.transposition,
          c.warp.speed,
          c.streaming,
          c.warp.awaitingAnalysis,
          getWarpEnabledAndPopulated(c.warp),
          c.loop.enabled,
          c.loop.startBeats,
          c.loop.endBeats,
        ]),
      ];
    })
    .concat(`${startBeats}-${endBeats}`)
    .join(',');
};

export default async function renderState({
  apiClient,
  title,
  state,
  fromStudioProjectId,
  projectId,
  lyrics,
  startBeats,
  endBeats,
  downbeats,
  cacheKey,
}: {
  apiClient: ApiClient;
  title?: string;
  state: StudioProjectState;
  fromStudioProjectId: string;
  projectId?: string;
  lyrics: string | null;
  startBeats: number;
  endBeats: number;
  downbeats: [number, number][];
  cacheKey?: string;
}): Promise<Clip> {
  try {
    let promise:
      | Promise<
          FetchResponse<
            paths['/api/studio/render-state']['post'],
            'post',
            `${string}/${string}`
          >
        >
      | undefined;

    if (cacheKey) {
      promise = renderStateCache[cacheKey];
    }
    if (!promise) {
      promise = apiClient.POST('/api/studio/render-state', {
        body: {
          title:
            title ||
            `${state.title || 'Untitled Project'}${state.editClipId ? ' (Edit)' : ''}`,
          lyrics,
          state: resolveHeardState(removeNonUploadedClips(state)),
          project_id: projectId,
          from_studio_project_id: fromStudioProjectId,
          start_beats: startBeats,
          end_beats: endBeats,
          downbeats,
          web_client_pathname: window.location.pathname,
        },
      });
      if (cacheKey) renderStateCache[cacheKey] = promise;
    }

    const result = await promise;

    if (!result.data) {
      console.error(result);
      throw new Error('Failed to render state');
    }

    // Check for moderation error in the response
    if (
      'moderation_error_message' in result.data &&
      result.data.moderation_error_message
    ) {
      throw new Error(
        `Moderation error: ${result.data.moderation_error_message}`
      );
    }

    return result.data as Clip;
  } catch (error) {
    toast({
      title: 'Something went wrong, try again',
      description: `If this error persists, please contact support.`,
      status: 'error',
      duration: 5000,
      isClosable: true,
    });
    throw error;
  }
}
