import { v4 as uuidv4 } from 'uuid';

import { StudioProjectState } from './fixStudioProjectState';
import { StudioTrackCore } from './fixTrack';

const EPSILON = 0.000001; // ~0.1 samples at 120bpm

/**
 * Final safety pass over a recently-changed project state to ensure:
 * - No undefined tracks
 * - No duplicate clip IDs
 * - Clips sorted by startBeats
 * - No zero-duration clips
 * - No overlapping clips
 * - ClipCreationIntents sorted
 */
export default function cleanState(
  state: StudioProjectState
): StudioProjectState {
  // Filter undefined tracks
  if ((state.tracks as any).includes(undefined)) {
    console.error('Found undefined track in state!!!');
    state.tracks = state.tracks.filter((t) => t !== undefined);
  }

  // Process all tracks and take lanes
  const processTrackCore = (trackCore: StudioTrackCore) => {
    // Remove zero-duration clips
    for (let i = trackCore.clips.length - 1; i >= 0; i--) {
      if (
        Math.abs(trackCore.clips[i].startBeats - trackCore.clips[i].endBeats) <
        EPSILON
      ) {
        trackCore.clips.splice(i, 1);
      } else if (
        trackCore.clips[i - 1] &&
        trackCore.clips[i].startBeats < trackCore.clips[i - 1].endBeats
      ) {
        // Trim overlapping clips
        trackCore.clips[i - 1] = {
          ...trackCore.clips[i - 1],
          endBeats: trackCore.clips[i].startBeats,
        };
      }
    }

    // Sort clips by startBeats
    trackCore.clips.sort((a, b) => a.startBeats - b.startBeats);

    // Final filter for zero-duration clips
    if (trackCore.clips.some((c) => c.endBeats <= c.startBeats)) {
      trackCore.clips = trackCore.clips.filter(
        (c) => c.endBeats > c.startBeats
      );
    }

    // Deduplicate clip IDs
    const usedIds: Record<string, boolean> = {};
    trackCore.clips.forEach((c) => {
      if (usedIds[c.id]) {
        console.error('Fixed duplicate clip id:', c.id);
        console.trace();
        c.id = uuidv4();
      }
      usedIds[c.id] = true;
    });

    // Sort clipCreationIntents
    trackCore.clipCreationIntents.sort((a, b) => {
      let diff = (a.startBeats ?? -Infinity) - (b.startBeats ?? -Infinity);
      if (diff === 0) {
        diff = (a.endBeats ?? -Infinity) - (b.endBeats ?? -Infinity);
      }
      return diff;
    });
  };

  state.tracks.forEach((track) => {
    processTrackCore(track);
    track.takeLanes.forEach((takeLane) => {
      processTrackCore(takeLane);
    });
  });

  return state;
}
