import JSZip from 'jszip'
import { parseSSC } from '../../game/sscParser'
import {
  AUDIO_EXTS,
  CHART_EXTS,
  IMAGE_EXTS,
  basename,
  easiestDifficulty,
  ext,
  generateId,
} from './file-utils'
import type { SongBundle } from './types'

export async function parseBundlesFromZip(zipFile: File): Promise<SongBundle[]> {
  const zip = await JSZip.loadAsync(zipFile)
  const entries = Object.entries(zip.files).filter(([, f]) => !f.dir)

  const dirs = new Map<string, Map<string, JSZip.JSZipObject>>()
  for (const [path, entry] of entries) {
    const parts = path.split('/')
    const fileName = parts.pop()
    if (!fileName) continue
    const dir = parts.join('/')
    if (!dirs.has(dir)) dirs.set(dir, new Map())
    dirs.get(dir)?.set(fileName, entry)
  }

  const bundles: SongBundle[] = []
  for (const [, fileMap] of dirs) {
    let chartEntry: JSZip.JSZipObject | null = null
    let chartName = ''

    for (const [name, entry] of fileMap) {
      if (CHART_EXTS.includes(ext(name))) {
        chartEntry = entry
        chartName = name
        break
      }
    }

    if (!chartEntry) continue

    const chartText = await chartEntry.async('string')
    const chart = parseSSC(chartText)
    if (chart.difficulties.length === 0) continue

    let audioName = ''
    if (chart.musicFile) {
      const musicBase = basename(chart.musicFile)
      for (const name of fileMap.keys()) {
        if (name.toLowerCase() === musicBase.toLowerCase()) {
          audioName = name
          break
        }
      }
    }
    if (!audioName) {
      for (const name of fileMap.keys()) {
        if (AUDIO_EXTS.includes(ext(name))) {
          audioName = name
          break
        }
      }
    }
    if (!audioName) continue

    let bgName = ''
    let bnName = ''
    for (const name of fileMap.keys()) {
      if (!IMAGE_EXTS.includes(ext(name))) continue
      if (/bg|background/i.test(name)) bgName = name
      else if (/bn|banner/i.test(name)) bnName = name
      else if (!bgName) bgName = name
    }

    const toFile = async (name: string, entry: JSZip.JSZipObject) => {
      const blob = await entry.async('blob')
      return new File([blob], name, { type: blob.type || 'application/octet-stream' })
    }

    const audioEntry = fileMap.get(audioName)
    if (!audioEntry) continue
    const audioFile = await toFile(audioName, audioEntry)
    const chartFile = await toFile(chartName, chartEntry)

    const bgEntry = bgName ? fileMap.get(bgName) : undefined
    const bnEntry = bnName ? fileMap.get(bnName) : undefined
    const bgFile = bgEntry ? await toFile(bgName, bgEntry) : null
    const bannerFile = bnEntry ? await toFile(bnName, bnEntry) : bgFile

    bundles.push({
      id: generateId(),
      chart,
      chartFile,
      audioFile,
      bgFile,
      bannerFile,
      selectedDifficulty: easiestDifficulty(chart),
      simplify: true,
    })
  }

  if (bundles.length === 0) throw new Error('No valid song bundles found in zip')
  return bundles
}
