import cleanState from './cleanState';
import fixLyricsCorrectionsByClipId, {
  LyricsCorrectionsByClipId,
} from './fixLyricsCorrectionsByClipId';
import fixMasterAmplitude from './fixMasterAmplitude';
import { fixCaption, fixMetadata, fixStyleSummary } from './fixMetadata';
import fixMetronome from './fixMetronome';
import fixProjectLoop from './fixProjectLoop';
import fixSelection, { StudioSelection } from './fixSelection';
import { fixSongFadeInBeats, fixSongFadeOutBeats } from './fixSongFades';
import fixTiming, { StudioTiming } from './fixTiming';
import fixTrack, { FixTrackConfig, StudioTrack } from './fixTrack';
import { maybeDeserializeProjectStateInPlace } from './serialization';
import { fixArrayInPlace, isArray, isString } from './utils';

export type StudioProjectState = {
  title?: string;
  caption?: string;
  styleSummary?: string;
  overrideLyrics?: string;
  editClipId?: string;
  metronome: {
    enabled: boolean;
    amplitude: number;
  };
  loop: {
    enabled: boolean;
    startBeats: number;
    endBeats: number;
  };
  timing: StudioTiming;
  tracks: StudioTrack[];
  selection: StudioSelection;
  songFadeInBeats: number;
  songFadeOutBeats: number;
  lyricsCorrectionsByClipId: LyricsCorrectionsByClipId;
  metadata?: {
    [key: string]: string | boolean | number | null | undefined;
  };
  amplitude: number;
};

export type FixStateInPlaceConfig = {
  wipeQueuedGenerationClipCreationIntents?: boolean;
  keepUploads?: string[];
};

export default function fixStudioProjectState(
  state: Partial<Record<string, unknown>>,
  config: FixStateInPlaceConfig
): StudioProjectState {
  if (typeof state !== 'object' || state === null) {
    state = {};
  }

  // Deserialize if state has markersRegistry (serialized format)
  state = maybeDeserializeProjectStateInPlace(
    state as any as StudioProjectState
  );
  state.amplitude = fixMasterAmplitude(state.amplitude);

  // Only fix optional fields if they exist in the input
  if ('caption' in state) {
    state.caption = fixCaption(state.caption);
  }
  if ('metadata' in state) {
    state.metadata = fixMetadata(state.metadata);
  }
  if ('styleSummary' in state) {
    state.styleSummary = fixStyleSummary(state.styleSummary);
  }

  state.lyricsCorrectionsByClipId = fixLyricsCorrectionsByClipId(
    state.lyricsCorrectionsByClipId
  );
  state.loop = fixProjectLoop(state.loop);
  state.metronome = fixMetronome(state.metronome);
  state.selection = fixSelection(state.selection);
  state.songFadeInBeats = fixSongFadeInBeats(state.songFadeInBeats);
  state.songFadeOutBeats = fixSongFadeOutBeats(state.songFadeOutBeats);

  // Fix tracks array in place
  if (!isArray(state.tracks)) {
    state.tracks = [];
  } else {
    const trackConfig: FixTrackConfig = {
      wipeQueuedGenerations: config.wipeQueuedGenerationClipCreationIntents,
      keepUploads: config.keepUploads,
    };
    fixArrayInPlace(state.tracks as unknown[], (track) =>
      fixTrack(track, trackConfig)
    );
  }

  // Fix timing now that we have valid track IDs
  const validTrackIds = (state.tracks as StudioTrack[])
    .filter((t) => isString(t.id))
    .map((t) => t.id);
  state.timing = fixTiming(state.timing, validTrackIds);

  // At time of writing, this should not actually change anything, but cleanState might be updated later.
  return cleanState(state as StudioProjectState);
}
