import { describe, expect, it } from 'vitest';

import fixTrack from './fixTrack';

describe('fixTrack', () => {
  it('should return null for invalid input', () => {
    expect(fixTrack(null)).toBe(null);
    expect(fixTrack(undefined)).toBe(null);
    expect(fixTrack('string')).toBe(null);
  });

  it('should return null for track without id', () => {
    const input = {
      name: 'Track',
      clips: [],
    };

    expect(fixTrack(input)).toBe(null);
  });

  it('should fix a minimal valid track', () => {
    const input = {
      id: 'track1',
    };

    const result = fixTrack(input);

    expect(result).not.toBe(null);
    expect(result?.id).toBe('track1');
    expect(result?.name).toBe('Track');
    expect(result?.clips).toEqual([]);
    expect(result?.clipCreationIntents).toEqual([]);
    expect(result?.takeLanes).toEqual([]);
  });

  it('should fix track in place', () => {
    const input = {
      id: 'track1',
      name: 'My Track',
      height: 150,
      clips: [],
      clipCreationIntents: [],
      solo: true,
      mute: false,
      arm: false,
      amplitude: 0.8,
      balance: -0.5,
      color: '#FF0000',
      input: null,
      instrument: { type: 'song' },
      soloTakeLaneId: null,
      takeLanes: [],
      takeLanesExpanded: false,
      eq: {
        enabled: true,
        band1: {
          type: 'highpass',
          enabled: false,
          frequency: 60,
          gain: 0,
          q: 0.707,
        },
        band2: {
          type: 'lowshelf',
          enabled: true,
          frequency: 160,
          gain: 0,
          q: 0.707,
        },
        band3: {
          type: 'peaking',
          enabled: true,
          frequency: 450,
          gain: 0,
          q: 0.707,
        },
        band4: {
          type: 'peaking',
          enabled: true,
          frequency: 1200,
          gain: 0,
          q: 0.707,
        },
        band5: {
          type: 'highshelf',
          enabled: true,
          frequency: 3200,
          gain: 0,
          q: 0.707,
        },
        band6: {
          type: 'lowpass',
          enabled: false,
          frequency: 8800,
          gain: 0,
          q: 0.707,
        },
      },
    };

    const result = fixTrack(input);

    expect(result).toBe(input); // Referential equality
    expect(result?.name).toBe('My Track');
    expect(result?.solo).toBe(true);
    expect(result?.amplitude).toBe(0.8);
  });

  it('should generate color from first clip if track color missing', () => {
    const input = {
      id: 'track1',
      clips: [
        {
          id: 'clip1',
          color: '#00FF00',
          startBeats: 0,
          endBeats: 4,
          clipId: 'content1',
          uploadId: null,
        },
      ],
    };

    const result = fixTrack(input);

    expect(result?.color).toBe('#00FF00');
  });

  it('should generate random color if no clips', () => {
    const input = {
      id: 'track1',
      clips: [],
    };

    const result = fixTrack(input);

    expect(result?.color).toBeDefined();
    expect(typeof result?.color).toBe('string');
    expect(result?.color).toMatch(/^#[0-9A-F]{6}$/i);
  });

  it('should sort clips by startBeats', () => {
    const input = {
      id: 'track1',
      clips: [
        {
          id: 'clip3',
          startBeats: 8,
          endBeats: 12,
          clipId: 'c3',
          uploadId: null,
        },
        {
          id: 'clip1',
          startBeats: 0,
          endBeats: 4,
          clipId: 'c1',
          uploadId: null,
        },
        {
          id: 'clip2',
          startBeats: 4,
          endBeats: 8,
          clipId: 'c2',
          uploadId: null,
        },
      ],
    };

    const result = fixTrack(input);

    expect(result?.clips.map((c) => c.id)).toEqual(['clip1', 'clip2', 'clip3']);
  });

  it('should close small gaps between clips', () => {
    const input = {
      id: 'track1',
      clips: [
        {
          id: 'clip1',
          startBeats: 0,
          endBeats: 4.00001, // Very slightly overlaps with next
          clipId: 'c1',
          uploadId: null,
        },
        {
          id: 'clip2',
          startBeats: 4,
          endBeats: 8,
          clipId: 'c2',
          uploadId: null,
        },
      ],
    };

    const result = fixTrack(input);

    expect(result?.clips[0].endBeats).toBe(4); // Trimmed to next clip's start
  });

  it('should filter clips with uploadId when keepUploads not provided', () => {
    const input = {
      id: 'track1',
      clips: [
        {
          id: 'clip1',
          startBeats: 0,
          endBeats: 4,
          clipId: 'content1',
          uploadId: null,
        },
        {
          id: 'clip2',
          startBeats: 4,
          endBeats: 8,
          clipId: null,
          uploadId: 'upload1',
        },
      ],
    };

    const result = fixTrack(input, {}); // No keepUploads

    expect(result?.clips).toHaveLength(1);
    expect(result?.clips[0].id).toBe('clip1');
  });

  it('should keep clips with valid uploadId when in keepUploads', () => {
    const input = {
      id: 'track1',
      clips: [
        {
          id: 'clip1',
          startBeats: 0,
          endBeats: 4,
          clipId: null,
          uploadId: 'upload1',
        },
        {
          id: 'clip2',
          startBeats: 4,
          endBeats: 8,
          clipId: null,
          uploadId: 'upload2',
        },
      ],
    };

    const result = fixTrack(input, { keepUploads: ['upload1'] });

    expect(result?.clips).toHaveLength(1);
    expect(result?.clips[0].id).toBe('clip1');
  });

  it('should deduplicate clip IDs', () => {
    const input = {
      id: 'track1',
      clips: [
        {
          id: 'clip1',
          startBeats: 0,
          endBeats: 4,
          clipId: 'c1',
          uploadId: null,
        },
        {
          id: 'clip1', // Duplicate ID
          startBeats: 4,
          endBeats: 8,
          clipId: 'c2',
          uploadId: null,
        },
      ],
    };

    const result = fixTrack(input);

    expect(result?.clips).toHaveLength(2);
    expect(result?.clips[0].id).toBe('clip1');
    expect(result?.clips[1].id).not.toBe('clip1'); // Should be regenerated
  });

  it('should remove zero-duration clips', () => {
    const input = {
      id: 'track1',
      clips: [
        {
          id: 'clip1',
          startBeats: 0,
          endBeats: 4,
          clipId: 'c1',
          uploadId: null,
        },
        {
          id: 'clip2',
          startBeats: 4,
          endBeats: 4, // Zero duration
          clipId: 'c2',
          uploadId: null,
        },
      ],
    };

    const result = fixTrack(input);

    expect(result?.clips).toHaveLength(1);
    expect(result?.clips[0].id).toBe('clip1');
  });

  it('should sort clipCreationIntents by startBeats and endBeats', () => {
    const input = {
      id: 'track1',
      clips: [],
      clipCreationIntents: [
        {
          id: 'intent3',
          possibleClipIds: ['c3'],
          queuedGenerationIds: [],
          name: 'Intent 3',
          namePersists: false,
          startBeats: 8,
          endBeats: 12,
          startTrimmedBeats: 0,
          endTrimmedBeats: 0,
        },
        {
          id: 'intent1',
          possibleClipIds: ['c1'],
          queuedGenerationIds: [],
          name: 'Intent 1',
          namePersists: false,
          startBeats: 0,
          endBeats: 4,
          startTrimmedBeats: 0,
          endTrimmedBeats: 0,
        },
        {
          id: 'intent2',
          possibleClipIds: ['c2'],
          queuedGenerationIds: [],
          name: 'Intent 2',
          namePersists: false,
          startBeats: 0,
          endBeats: 8,
          startTrimmedBeats: 0,
          endTrimmedBeats: 0,
        },
      ],
    };

    const result = fixTrack(input);

    // Should sort by startBeats, then endBeats
    expect(result?.clipCreationIntents.map((c) => c.id)).toEqual([
      'intent1',
      'intent2',
      'intent3',
    ]);
  });
});
