const API_BASE = (process.env.UDIOAPI_BASE_URL || 'https://udioapi.pro/api').replace(/\/$/, '')
const API_KEY = process.env.UDIOAPI_KEY
const POLL_INTERVAL_MS = 5_000
const MAX_POLLS = 60

if (!API_KEY) {
  throw new Error('Set UDIOAPI_KEY in the server environment before running this example.')
}

const sleep = (milliseconds) => new Promise(resolve => setTimeout(resolve, milliseconds))

async function requestJson(path, options = {}) {
  const response = await fetch(API_BASE + path, options)
  const text = await response.text()
  let payload

  try {
    payload = text ? JSON.parse(text) : {}
  } catch {
    throw new Error('The API returned a non-JSON response with HTTP ' + response.status + '.')
  }

  if (!response.ok || Number(payload.code) >= 400) {
    const message = payload.message || payload.error || response.statusText || 'API request failed'
    const error = new Error('HTTP ' + response.status + ': ' + message)
    error.status = response.status
    error.payload = payload
    throw error
  }

  return payload
}

function readWorkId(payload) {
  return payload.workId
    || payload.task_id
    || payload.data?.workId
    || payload.data?.task_id
}

function inspectTask(payload) {
  const data = payload.data || payload
  const status = String(data.status || '').toUpperCase()
  const items = Array.isArray(data.response_data)
    ? data.response_data
    : Array.isArray(data.items)
      ? data.items
      : []

  const failedItem = items.find(item => item?.fail_message || item?.error_message)
  const terminalFailure = ['FAILED', 'ERROR'].includes(status)
  if (failedItem || terminalFailure) {
    return {
      done: true,
      error: failedItem?.fail_message
        || failedItem?.error_message
        || data.fail_message
        || data.error_message
        || data.message
        || (typeof data.error === 'string' ? data.error : null)
        || 'Music task ended with status ' + status,
      audioUrls: [],
    }
  }

  const audioUrls = [...new Set(items.map(item => item?.audio_url).filter(Boolean))]
  const terminalMessage = items.some(item =>
    String(item?.extra_message || '').toLowerCase().includes('all generated successfully'),
  )
  const terminalStatus = ['SUCCESS', 'COMPLETED', 'COMPLETE'].includes(status)

  return {
    done: terminalMessage || terminalStatus || audioUrls.length >= 2,
    error: null,
    audioUrls,
  }
}

async function main() {
  const created = await requestJson('/v2/generate', {
    method: 'POST',
    headers: {
      Authorization: 'Bearer ' + API_KEY,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      model: 'chirp-v5-5',
      gpt_description_prompt: 'Warm indie pop with acoustic guitar and a hopeful chorus',
      make_instrumental: true,
    }),
  })

  const workId = readWorkId(created)
  if (!workId) {
    throw new Error('The create response did not include workId or task_id.')
  }

  console.log('Created music task:', workId)

  for (let attempt = 1; attempt <= MAX_POLLS; attempt += 1) {
    const status = await requestJson('/v2/feed?workId=' + encodeURIComponent(workId), {
      headers: { Authorization: 'Bearer ' + API_KEY },
    })
    const result = inspectTask(status)

    if (result.error) {
      throw new Error('Music task failed: ' + result.error)
    }
    if (result.done) {
      console.log('Completed audio URLs:', result.audioUrls)
      return
    }

    console.log('Still processing (' + attempt + '/' + MAX_POLLS + ')')
    await sleep(POLL_INTERVAL_MS)
  }

  throw new Error('Polling timed out. Save the workId and query the feed endpoint again later.')
}

main().catch((error) => {
  console.error(error.message)
  process.exitCode = 1
})
