import { StudioProjectState } from './types';

interface StateChangeResult {
  hasEditChanges: boolean;
  changedProperties: string[];
  changeType: 'tracks' | 'clips' | 'timing' | 'generation' | 'ui' | 'mixed';
}

// Properties that are protected in read-only mode
const PROTECTED_PROPERTIES = [
  'tracks', // Track creation/deletion/modification
  'timing', // BPM, timing changes
  'songFadeInBeats', // Song-level edits
  'songFadeOutBeats',
  'metronome', // Metronome state changes
] as const;

// Track properties that are protected
const PROTECTED_TRACK_PROPERTIES = [
  'clips',
  'name',
  'color',
  'amplitude',
  'balance',
  'mute',
  'solo',
  'arm',
  'input',
  'height',
] as const;

// Clip properties that are protected
const PROTECTED_CLIP_PROPERTIES = [
  'startBeats',
  'endBeats',
  'clipId',
  'amplitude',
  'transposition',
  'fadeInBeats',
  'fadeOutBeats',
  'warp',
  'loop',
] as const;

/**
 * Detects what changed between two states to determine if edit operations occurred
 * This is more reliable than trying to analyze the operation function itself
 */
export const detectStateChanges = (
  currentState: StudioProjectState,
  newState: StudioProjectState
): StateChangeResult => {
  const changedProperties: string[] = [];

  // Check top-level protected properties
  for (const prop of PROTECTED_PROPERTIES) {
    if (JSON.stringify(currentState[prop]) !== JSON.stringify(newState[prop])) {
      changedProperties.push(prop);
    }
  }

  // Check track-level changes
  if (currentState.tracks.length !== newState.tracks.length) {
    changedProperties.push('tracks.length');
  } else {
    // Check each track for changes
    for (let i = 0; i < currentState.tracks.length; i++) {
      const currentTrack = currentState.tracks[i];
      const newTrack = newState.tracks[i];

      if (!currentTrack || !newTrack) {
        changedProperties.push(`tracks[${i}]`);
        continue;
      }

      // Check track properties
      for (const prop of PROTECTED_TRACK_PROPERTIES) {
        if (
          JSON.stringify(currentTrack[prop]) !== JSON.stringify(newTrack[prop])
        ) {
          changedProperties.push(`tracks[${i}].${prop}`);
        }
      }

      // Deep check clips if they exist
      if (currentTrack.clips && newTrack.clips) {
        if (currentTrack.clips.length !== newTrack.clips.length) {
          changedProperties.push(`tracks[${i}].clips.length`);
        } else {
          // Check each clip
          for (let j = 0; j < currentTrack.clips.length; j++) {
            const currentClip = currentTrack.clips[j];
            const newClip = newTrack.clips[j];

            if (!currentClip || !newClip) {
              changedProperties.push(`tracks[${i}].clips[${j}]`);
              continue;
            }

            for (const clipProp of PROTECTED_CLIP_PROPERTIES) {
              if (
                JSON.stringify(currentClip[clipProp]) !==
                JSON.stringify(newClip[clipProp])
              ) {
                changedProperties.push(`tracks[${i}].clips[${j}].${clipProp}`);
              }
            }
          }
        }
      }
    }
  }

  // Determine change type and if changes are edit operations
  const hasEditChanges = changedProperties.length > 0;
  const changeType = determineChangeType(changedProperties);

  return { hasEditChanges, changedProperties, changeType };
};

/**
 * Categorize the type of changes that occurred
 */
const determineChangeType = (
  changedProps: string[]
): StateChangeResult['changeType'] => {
  const hasTrackChanges = changedProps.some(
    (p) => p.startsWith('tracks') && !p.includes('clips')
  );
  const hasClipChanges = changedProps.some((p) => p.includes('clips'));
  const hasTimingChanges = changedProps.some((p) => p.includes('timing'));
  const hasGenerationChanges = changedProps.some((p) =>
    p.includes('queuedGenerations')
  );

  const changeTypes = [
    hasTrackChanges,
    hasClipChanges,
    hasTimingChanges,
    hasGenerationChanges,
  ].filter(Boolean).length;

  if (changeTypes > 1) return 'mixed';
  if (hasTrackChanges) return 'tracks';
  if (hasClipChanges) return 'clips';
  if (hasTimingChanges) return 'timing';
  if (hasGenerationChanges) return 'generation';

  return 'ui';
};

/**
 * Generate user-friendly messages based on change type
 */
export const getEditBlockMessage = (
  changeType: StateChangeResult['changeType'],
  _changedProperties: string[]
): string => {
  switch (changeType) {
    case 'tracks':
      return 'Cannot modify tracks in shared projects. Clone to edit tracks.';
    case 'clips':
      return 'Cannot edit clips in shared projects. Clone to make clip changes.';
    case 'timing':
      return 'Cannot change timing/tempo in shared projects. Clone to edit timing.';
    case 'generation':
      return 'Cannot generate new content in shared projects. Clone to create music.';
    case 'mixed':
      return 'Cannot make edits to shared projects. Clone to modify tracks and clips.';
    default:
      return 'Cannot make changes to shared projects. Clone to edit.';
  }
};

/**
 * Check if a specific property path should be protected
 */
export const isProtectedProperty = (propertyPath: string): boolean => {
  // Check direct protected properties
  if (PROTECTED_PROPERTIES.some((prop) => propertyPath.startsWith(prop))) {
    return true;
  }

  // Check track-specific properties
  if (
    PROTECTED_TRACK_PROPERTIES.some((prop) => propertyPath.includes(`.${prop}`))
  ) {
    return true;
  }

  // Check clip-specific properties
  if (
    PROTECTED_CLIP_PROPERTIES.some((prop) => propertyPath.includes(`.${prop}`))
  ) {
    return true;
  }

  return false;
};
