import {
  isArray,
  isBoolean,
  isNonNegativeFiniteNumber,
  isObject,
  isValidFiniteNumber,
  makeFixer,
} from './utils';

export type EQBandType =
  | 'highpass'
  | 'lowshelf'
  | 'peaking'
  | 'notch'
  | 'highshelf'
  | 'lowpass';

export type EQBand = {
  type: EQBandType;
  enabled: boolean;
  frequency: number;
  gain: number;
  q: number;
};

export type TrackEQ = {
  enabled: boolean;
  band1: EQBand;
  band2: EQBand;
  band3: EQBand;
  band4: EQBand;
  band5: EQBand;
  band6: EQBand;
};

// Default EQ bands with neutral settings
export const DEFAULT_EQ_BANDS: EQBand[] = [
  { type: 'highpass', enabled: true, frequency: 60, gain: 0, q: 0.707 },
  { type: 'lowshelf', enabled: true, frequency: 160, gain: 0, q: 0.707 },
  { type: 'peaking', enabled: true, frequency: 450, gain: 0, q: 0.707 },
  { type: 'peaking', enabled: true, frequency: 1200, gain: 0, q: 0.707 },
  { type: 'highshelf', enabled: true, frequency: 3200, gain: 0, q: 0.707 },
  { type: 'lowpass', enabled: true, frequency: 8800, gain: 0, q: 0.707 },
];

export const DISABLED_EQ_BANDS: EQBand[] = DEFAULT_EQ_BANDS.map((x) => ({
  ...x,
  enabled: false,
}));

const fixEQBandType = (type: unknown, defaultType: EQBandType): EQBandType => {
  if (
    type === 'highpass' ||
    type === 'lowshelf' ||
    type === 'peaking' ||
    type === 'notch' ||
    type === 'highshelf' ||
    type === 'lowpass'
  ) {
    return type;
  }
  return defaultType;
};

const fixEQBandEnabled = makeFixer(isBoolean, true);
const fixEQBandFrequency = makeFixer(isValidFiniteNumber, 1000);
const fixEQBandGain = makeFixer(isValidFiniteNumber, 0);
const fixEQBandQ = makeFixer(isValidFiniteNumber, 0.707);

export const fixEQBand = (band: unknown, defaultBand: EQBand): EQBand => {
  if (!isObject(band)) {
    return { ...defaultBand };
  }

  const b = band as {
    type: unknown;
    enabled: unknown;
    frequency: unknown;
    gain: unknown;
    q: unknown;
  };

  b.type = fixEQBandType(b.type, defaultBand.type);
  b.enabled = fixEQBandEnabled(b.enabled);
  // Use the defaultBand's values as fallbacks instead of generic defaults
  // Frequency and q must be non-negative
  b.frequency = isNonNegativeFiniteNumber(b.frequency)
    ? b.frequency
    : defaultBand.frequency;
  b.gain = isValidFiniteNumber(b.gain) ? b.gain : defaultBand.gain;
  b.q = isNonNegativeFiniteNumber(b.q) ? b.q : defaultBand.q;

  return band as EQBand;
};

// Legacy FilterBand type for migration
type LegacyFilterBand = {
  enabled: boolean;
  frequency: number;
  q: number;
};

export default function fixTrackEQ(eq: unknown): TrackEQ {
  // Check if this is an old array-based EQ or has old filter properties
  const isOldFormat =
    isArray(eq) ||
    (isObject(eq) && ((eq as any).highPassFilter || (eq as any).lowPassFilter));

  if (isOldFormat) {
    // For array-based EQ, we need to create a new object since we can't mutate an array into an object
    if (isArray(eq)) {
      const oldEQ = eq as any[];
      return {
        enabled: true,
        band1: DISABLED_EQ_BANDS[0],
        band2: oldEQ[0]
          ? {
              type: 'peaking',
              enabled: true,
              frequency: fixEQBandFrequency(oldEQ[0].frequency),
              gain: fixEQBandGain(oldEQ[0].gain),
              q: fixEQBandQ(oldEQ[0].q),
            }
          : DEFAULT_EQ_BANDS[1],
        band3: oldEQ[1]
          ? {
              type: 'peaking',
              enabled: true,
              frequency: fixEQBandFrequency(oldEQ[1].frequency),
              gain: fixEQBandGain(oldEQ[1].gain),
              q: fixEQBandQ(oldEQ[1].q),
            }
          : DEFAULT_EQ_BANDS[2],
        band4: oldEQ[2]
          ? {
              type: 'peaking',
              enabled: true,
              frequency: fixEQBandFrequency(oldEQ[2].frequency),
              gain: fixEQBandGain(oldEQ[2].gain),
              q: fixEQBandQ(oldEQ[2].q),
            }
          : DEFAULT_EQ_BANDS[3],
        band5: oldEQ[3]
          ? {
              type: 'peaking',
              enabled: true,
              frequency: fixEQBandFrequency(oldEQ[3].frequency),
              gain: fixEQBandGain(oldEQ[3].gain),
              q: fixEQBandQ(oldEQ[3].q),
            }
          : DEFAULT_EQ_BANDS[4],
        band6: DISABLED_EQ_BANDS[5],
      };
    }

    // Migrate from old object format with highPassFilter/lowPassFilter
    const e = eq as any;
    const oldHighPass = e.highPassFilter as LegacyFilterBand | undefined;
    const oldLowPass = e.lowPassFilter as LegacyFilterBand | undefined;

    e.enabled = true;

    // Band 1: High-pass if old filter was enabled, otherwise disabled
    e.band1 = oldHighPass?.enabled
      ? {
          type: 'highpass',
          enabled: true,
          frequency: fixEQBandFrequency(oldHighPass.frequency),
          gain: 0,
          q: fixEQBandQ(oldHighPass.q),
        }
      : DISABLED_EQ_BANDS[0];

    // Bands 2-5: Use defaults
    e.band2 = DEFAULT_EQ_BANDS[1];
    e.band3 = DEFAULT_EQ_BANDS[2];
    e.band4 = DEFAULT_EQ_BANDS[3];
    e.band5 = DEFAULT_EQ_BANDS[4];

    // Band 6: Low-pass if old filter was enabled, otherwise disabled
    e.band6 = oldLowPass?.enabled
      ? {
          type: 'lowpass',
          enabled: true,
          frequency: fixEQBandFrequency(oldLowPass.frequency),
          gain: 0,
          q: fixEQBandQ(oldLowPass.q),
        }
      : DISABLED_EQ_BANDS[5];

    // Clean up old properties
    delete e.highPassFilter;
    delete e.lowPassFilter;

    return eq as TrackEQ;
  }

  if (!isObject(eq)) {
    return {
      enabled: true,
      band1: DISABLED_EQ_BANDS[0],
      band2: DEFAULT_EQ_BANDS[1],
      band3: DEFAULT_EQ_BANDS[2],
      band4: DEFAULT_EQ_BANDS[3],
      band5: DEFAULT_EQ_BANDS[4],
      band6: DISABLED_EQ_BANDS[5],
    };
  }

  const e = eq as {
    enabled: unknown;
    band1: unknown;
    band2: unknown;
    band3: unknown;
    band4: unknown;
    band5: unknown;
    band6: unknown;
  };

  // Check if we need to migrate from 4-band to 6-band structure
  if (e.band5 === undefined) {
    const oldEQ = eq as any;
    return {
      enabled: fixEQBandEnabled(oldEQ.enabled ?? true),
      band1: fixEQBand(oldEQ.band1, DISABLED_EQ_BANDS[0]),
      band2: fixEQBand(oldEQ.band2, DEFAULT_EQ_BANDS[1]),
      band3: fixEQBand(oldEQ.band3, DEFAULT_EQ_BANDS[2]),
      band4: fixEQBand(oldEQ.band4, DEFAULT_EQ_BANDS[3]),
      band5: DEFAULT_EQ_BANDS[4],
      band6: DISABLED_EQ_BANDS[5],
    };
  }

  // Fix all bands in place
  e.enabled = fixEQBandEnabled(e.enabled);
  e.band1 = fixEQBand(e.band1, DEFAULT_EQ_BANDS[0]);
  e.band2 = fixEQBand(e.band2, DEFAULT_EQ_BANDS[1]);
  e.band3 = fixEQBand(e.band3, DEFAULT_EQ_BANDS[2]);
  e.band4 = fixEQBand(e.band4, DEFAULT_EQ_BANDS[3]);
  e.band5 = fixEQBand(e.band5, DEFAULT_EQ_BANDS[4]);
  e.band6 = fixEQBand(e.band6, DEFAULT_EQ_BANDS[5]);

  return eq as TrackEQ;
}
