import { tagsToArray, tagsToNegativeTags } from '@/components/song/songUtils';
import { Clip } from '@/state/clipStore';

/**
 * Clip schema, but allows the fields to be optional
 */
export type PartialClip<K extends keyof Clip = never> = Pick<Clip, K> & {
  [P in Exclude<keyof Clip, K>]?: Clip[P] | null;
};

/**
 * Determines if the clip is disqualified from publishing because of vocals
 */
export function isUnpublishableUploadClip(clip: Clip) {
  if (!clip.metadata.has_vocal) {
    return false;
  }

  if (clip.metadata.can_publish_with_vocal) {
    return false;
  }

  return true;
}

/**
 * Determines whether the clip is a stem
 */
export function isStemClip(clip: PartialClip | null) {
  return clip?.metadata?.type === 'stem';
}

/**
 * Determines if the model version of the clip is valid
 */
export const isValidModelVersion = (clip: PartialClip | null) => {
  return (
    clip?.major_model_version === 'v2' ||
    clip?.major_model_version === 'v3' ||
    clip?.major_model_version === 'v3.5' ||
    clip?.major_model_version === 'v4' ||
    clip?.major_model_version === 'v4.5' ||
    clip?.model_name?.includes('auk') ||
    clip?.model_name?.includes('bluejay') ||
    clip?.model_name?.includes('crow') ||
    ['upload', 'edit_crop', 'edit_fade', 'edit_v3_export'].includes(
      clip?.metadata?.type || ''
    )
  );
};

/**
 * Determines if the clip is complete
 */
export function isComplete(clip: PartialClip | null) {
  return clip?.status === 'complete';
}

/**
 * Determines if the get full song should be shown
 */
export function shouldShowGetFullSong(
  clip: PartialClip | null,
  userId?: string
) {
  const isInvalidModel = !isValidModelVersion(clip);
  const isNotOwner = userId !== clip?.user_id;
  const hasNoHistory = !clip?.metadata?.history;
  const isInfill = Boolean(clip?.metadata?.infill);
  const isStem = clip?.metadata?.type === 'stem';

  return !Boolean(
    isInvalidModel || isNotOwner || hasNoHistory || isInfill || isStem
  );
}

/**
 * Removes [bracketed] text and trims to a max length
 */
export function formatClipPrompt(prompt: string, maxLength = 40) {
  return prompt
    .replace(/\[.*?\]/g, '')
    .trim()
    .split('\n')[0]
    .slice(0, maxLength);
}

/**
 * Chooses between title, prompt, or a fallback
 */
export function formatClipTitle(
  title: string | null | undefined,
  prompt: string | null | undefined,
  fallback = 'Untitled'
) {
  return title || formatClipPrompt(prompt ?? '') || fallback;
}

/**
 * Gets the title from a (loose) clip entity
 */
export const getClipTitle = (clip: PartialClip | null | undefined) =>
  formatClipTitle(clip?.title, clip?.metadata?.prompt, 'Untitled');

/**
 * Gets the extend task from a clip
 */
export function getExtendTask(clip: Clip) {
  if (clip.metadata.type === 'upload') {
    return 'upload_extend';
  }
  return 'extend';
}

/**
 * Clip types for which volume normalization should be disabled
 */
export function clipEditsDisableVolumeNormalization(clip: Clip) {
  return [
    'upload',
    'upload_extend',
    'rendered_context_window',
    'studio_export',
    'edit_v3_export',
  ].includes(clip.metadata.type ?? '');
}

/**
 * Gets the display tags for a clip
 */
export function getClipDisplayTags(clip?: PartialClip | null) {
  return clip?.display_tags || clip?.metadata?.tags || '';
}

/**
 * Gets the full tags for a clip as an array
 */
export function getClipFullTags(clip?: PartialClip | null) {
  return [
    ...tagsToArray(clip?.metadata?.tags || clip?.display_tags || ''),
    ...tagsToNegativeTags(tagsToArray(clip?.metadata?.negative_tags || '')),
  ];
}

/**
 * Gets the full tags for a clip as a string
 */
export function getClipFullTagsString(clip?: PartialClip | null) {
  return getClipFullTags(clip).join(', ');
}
