import { ApiClient } from '@/lib/apiClient';
import { Clip } from '@/state/clipStore';

import { getDownbeatsFromTiming } from '../components/edit2025/getTimingFromDownbeats';
import createStudioClipSync from '../components/studio/createStudioClipSync';
import createTrack from '../components/studio/createTrack';
import DEFAULT_STATE from '../components/studio/defaultState';
import {
  getDerivedTiming,
  getSongEndBeats,
  getSongStartBeats,
} from '../components/studio/selectors';
import { StudioProjectState } from '../components/studio/types';
import { downloadMultitrackV2 } from './download';
import snap from './snap';

export interface ConstructStemsZipPayloadParams {
  rootClipName: string;
  clips: Clip[];
  durationSeconds: number;
  pathname: string;
  downbeats?: [number, number][];
  format?: 'mp3' | 'wav';
  useTempoLocked?: boolean;
}

/**
 * Constructs the payload for downloading stems as a zip file.
 *
 * @param params - Configuration object for constructing stems zip payload
 * @param params.rootClipName - The name to use for the exported file
 * @param params.clips - Array of clips to include in the export
 * @param params.durationSeconds - Duration of the clips in seconds
 * @param params.pathname - The web client pathname
 * @param params.downbeats - Optional downbeats array for timing alignment
 * @param params.format - Audio format for the export (mp3 or wav), defaults to 'wav'
 * @param params.useTempoLocked - If true, uses manual tempo from downbeats average instead of follow-track, defaults to false
 * @returns The render state body payload for the multitrack download
 */
export function constructStemsZipPayload({
  rootClipName,
  clips,
  durationSeconds,
  pathname,
  downbeats,
  format = 'wav',
  useTempoLocked = false,
}: ConstructStemsZipPayloadParams) {
  // Use provided downbeats or create a fallback
  const effectiveDownbeats = downbeats ?? [
    [0, 0],
    [durationSeconds, durationSeconds],
  ];

  // Name map for stem types
  const nameMap: Record<string, string> = {
    Vocals: 'Lead Vocals',
    Backing_Vocals: 'Backing Vocals',
    Drums: 'Drums',
    Bass: 'Bass',
    Guitar: 'Guitar',
    Keyboard: 'Keyboard',
    Percussion: 'Percussion',
    Strings: 'Strings',
    Synth: 'Synth',
    FX: 'Other',
    Brass: 'Brass',
    Woodwinds: 'Woodwinds',
  };

  // Create a studio clip for each clip
  const tracks = clips.map((clip, index) => {
    const { clip: studioClip } = createStudioClipSync(
      { id: clip.id, metadata: { duration: durationSeconds } },
      effectiveDownbeats,
      durationSeconds
    );

    // Use stem type name if available, otherwise fall back to clip title or Track N
    const stemTypeName = clip.metadata?.stem_type_group_name;
    const trackName = stemTypeName
      ? (nameMap[stemTypeName] ?? stemTypeName.replace('_', ' '))
      : clip.title || `Track ${index + 1}`;

    const track = createTrack(false);
    return {
      ...track,
      name: trackName,
      clips: [studioClip],
    };
  });

  // Validate that we have at least one track
  if (tracks.length === 0) {
    throw new Error('Cannot create stems zip: no clips provided');
  }

  // Calculate average BPS from downbeats if using tempo-locked mode
  const calculateAverageBPS = (downbeats: [number, number][]): number => {
    if (downbeats.length < 2) return 2; // Fallback to 2 BPS (120 BPM)

    const firstPoint = downbeats[0];
    const lastPoint = downbeats[downbeats.length - 1];

    const totalBeats = downbeats.length - 1;
    const totalSeconds = lastPoint[0] - firstPoint[0];

    if (totalSeconds === 0) return 2; // Avoid division by zero

    return totalBeats / totalSeconds;
  };

  // Determine timing based on whether downbeats were provided and mode
  const timing =
    downbeats && useTempoLocked
      ? {
          type: 'manual' as const,
          bps: snap(calculateAverageBPS(downbeats), 1 / 60),
          bpsAutomation: [],
          lockBPS: true,
        }
      : downbeats
        ? {
            type: 'follow-track' as const,
            trackId: tracks[0]?.id ?? '',
            fallbackBPS: 2,
          }
        : {
            type: 'manual' as const,
            bps: 1,
            bpsAutomation: [],
            lockBPS: true,
          };

  // Construct the studio project state
  const state: StudioProjectState = {
    ...DEFAULT_STATE,
    timing,
    tracks,
  };

  const startBeats = getSongStartBeats(state);
  const endBeats = getSongEndBeats(state);
  const derivedTiming = getDerivedTiming(state);

  const finalTitle =
    useTempoLocked && timing.type === 'manual' && timing.bps
      ? `${rootClipName} (${timing.bps * 60}BPM)`
      : rootClipName;

  // Build the render state body
  const renderStateBody = {
    title: finalTitle,
    lyrics: '',
    tags: '',
    negative_tags: '',
    style_summary: '',
    caption: '',
    state: {
      ...state,
      timing: {
        type: 'manual' as const,
        ...derivedTiming,
      },
    },
    start_beats: startBeats,
    end_beats: endBeats,
    project_id: undefined,
    web_client_pathname: pathname,
    downbeats: getDownbeatsFromTiming(derivedTiming, startBeats, endBeats),
    format: (format === 'wav' ? 'wav_s16' : format) as
      | 'mp3'
      | 'wav'
      | 'wav_s16',
  };

  return renderStateBody;
}

export interface DownloadStemsAsZipParams {
  apiClient: ApiClient;
  rootClipName: string;
  clips: Clip[];
  durationSeconds: number;
  downbeats?: [number, number][];
  format?: 'mp3' | 'wav';
  useTempoLocked?: boolean;
}

/**
 * Downloads multiple clips as a multitrack zip file.
 * Each clip will be arranged on a separate track in the exported project.
 *
 * @param params - Configuration object for downloading stems as zip
 * @param params.apiClient - The API client for making requests
 * @param params.rootClipName - The name to use for the exported file
 * @param params.clips - Array of clips to include in the export
 * @param params.durationSeconds - Duration of the clips in seconds
 * @param params.downbeats - Optional downbeats array for timing alignment
 * @param params.format - Audio format for the export (mp3 or wav), defaults to 'wav'
 * @param params.useTempoLocked - If true, uses manual tempo from downbeats average instead of follow-track, defaults to false
 */
export default async function downloadStemsAsZip({
  apiClient,
  rootClipName,
  clips,
  durationSeconds,
  downbeats,
  format = 'wav',
  useTempoLocked = false,
}: DownloadStemsAsZipParams): Promise<void> {
  const renderStateBody = constructStemsZipPayload({
    rootClipName,
    clips,
    durationSeconds,
    pathname: window.location.pathname,
    downbeats,
    format,
    useTempoLocked,
  });

  // Trigger the multitrack download
  await downloadMultitrackV2(apiClient, renderStateBody);
}
