import { SerializedProjectState } from '@suno/studiokit/projectState/serialization';

import { ApiClient } from '@/lib/apiClient';
import { components } from '@/lib/gen';

// Typed responses from API
export type ProjectData = components['schemas']['StudioProjectSchema'];
export type RevisionData = components['schemas']['StudioVersionStateSchema'];
export type ProjectVersionData = components['schemas']['ProjectVersionSchema'];

/**
 * Custom error class for Studio Project API calls
 */
export class StudioProjectApiError extends Error {
  constructor(
    message: string,
    public status?: number,
    public originalError?: unknown
  ) {
    super(message);
    this.name = 'StudioProjectApiError';
  }
}

/**
 * Handles API errors consistently across all project API calls
 */
function handleApiError(error: unknown, defaultMessage: string): never {
  console.error(defaultMessage, error);

  let status: number | undefined;
  if (error && typeof error === 'object' && 'status' in error) {
    status = error.status as number;
  }

  throw new StudioProjectApiError(defaultMessage, status, error);
}

/**
 * Fetches a project by its ID
 */
export async function fetchProjectById(
  apiClient: ApiClient,
  projectId: string
): Promise<ProjectData> {
  try {
    const response = await apiClient.GET('/api/studio/project/{project_id}', {
      params: {
        path: {
          project_id: projectId,
        },
      },
    });

    if (!response.data || typeof response.data !== 'object') {
      throw new Error('No response data');
    }

    return response.data as ProjectData;
  } catch (error) {
    handleApiError(error, 'Failed to fetch project');
  }
}

/**
 * Fetches a project revision by its ID
 */
export async function fetchRevisionById(
  apiClient: ApiClient,
  revisionId: string
): Promise<ProjectVersionData> {
  try {
    const response = await apiClient.GET(
      '/api/studio/project_revision/{revision_id}',
      {
        params: {
          path: {
            revision_id: revisionId,
          },
        },
      }
    );

    if (!response.data) {
      throw new Error('No revision data found');
    }

    return response.data as ProjectVersionData;
  } catch (error) {
    handleApiError(error, 'Failed to fetch project revision');
  }
}

/**
 * Fetches a specific version of a project
 */
export async function fetchProjectVersion(
  apiClient: ApiClient,
  projectId: string,
  versionId: string
): Promise<RevisionData> {
  try {
    const response = await apiClient.GET(
      '/api/studio/{project_id}/version/{version_id}',
      {
        params: {
          path: {
            project_id: projectId,
            version_id: versionId,
          },
        },
      }
    );

    if (!response.data) {
      throw new Error('Version data not found');
    }

    return response.data;
  } catch (error) {
    handleApiError(error, 'Failed to fetch project version');
  }
}

/**
 * Creates a new project
 */
export async function createProject(
  apiClient: ApiClient,
  title: string = 'Untitled Project'
): Promise<{ id: string; title: string }> {
  try {
    const response = await apiClient.POST('/api/studio/create-project', {
      params: {
        query: { title },
      } as const,
    });

    if (
      !response.data ||
      typeof response.data !== 'object' ||
      !('id' in response.data)
    ) {
      throw new Error('Invalid response format');
    }

    return {
      id: response.data.id as string,
      title: (response.data.title as string) || 'Untitled Project',
    };
  } catch (error) {
    handleApiError(error, 'Failed to create project');
  }
}

/**
 * Saves a project and returns its version ID
 */
export async function saveProject(
  apiClient: ApiClient,
  projectId: string,
  state: SerializedProjectState,
  title: string
): Promise<string> {
  try {
    const response = await apiClient.POST('/api/studio/save-project', {
      body: {
        project_id: projectId,
        state,
        title,
      } as const,
    });

    const versionId = response.data?.version_id;
    if (!versionId) {
      throw new Error('No version ID in save response');
    }

    return versionId as string;
  } catch (error) {
    handleApiError(error, 'Failed to save project');
  }
}

/**
 * Creates or loads a project for a given clip ID
 */
export async function createOrLoadProjectForClip(
  apiClient: ApiClient,
  clipId: string
): Promise<string> {
  try {
    const response = await apiClient.POST(
      '/api/studio/create-or-load-project-for-clip/{clip_id}',
      {
        params: {
          path: {
            clip_id: clipId,
          },
        },
      }
    );

    if (!response.data?.id) {
      throw new Error('No project ID in response');
    }

    return response.data.id as string;
  } catch (error) {
    handleApiError(error, 'Failed to create or load project for clip');
  }
}

/**
 * Clones a project from a revision ID
 */
export async function cloneProject(
  apiClient: ApiClient,
  revisionId: string,
  title?: string
): Promise<string> {
  try {
    const response = await apiClient.POST(
      '/api/studio/project_revision/{revision_id}/clone',
      {
        params: {
          path: {
            revision_id: revisionId,
          },
        },
        body: {
          title: title,
        },
      }
    );

    if (!response.data?.id) {
      throw new Error('No project ID in cloned project response');
    }

    return response.data.id as string;
  } catch (error) {
    handleApiError(error, 'Failed to clone project');
  }
}
