import {
  abortMultipartUpload,
  completeMultipartUpload,
  getMultipartUploadStatus,
  initMultipartUpload,
  putMultipartChunk,
  uploadSiteAsset
} from '../api/admin'

const CHUNK_THRESHOLD = 8 * 1024 * 1024
const DEFAULT_CHUNK_SIZE = 4 * 1024 * 1024
// 串行上传分片，避免 HTTP/2 多路复用 + 大 body 在部分反代上不稳定
const UPLOAD_CONCURRENCY = 1

function fileFingerprint(file) {
  return `${file.name}\t${file.size}\t${file.lastModified}`
}

function storageKey(siteId) {
  return `yh_resumable_asset_${siteId}`
}

/**
 * 站点资源上传：小于阈值整包 POST；大于等于阈值走分片 + sessionStorage 断点续传（同文件同站点刷新后可续传）。
 * @param {string} siteId
 * @param {File|Blob} file
 * @param {{ folder?: string, downloadable?: boolean, preserveFilename?: boolean }} opts
 * @param {{ onProgress?: (p: { percent: number, loaded: number, total: number }) => void }} callbacks
 */
export async function uploadSiteAssetWithResume(siteId, file, opts = {}, callbacks = {}) {
  const onProgress = typeof callbacks.onProgress === 'function' ? callbacks.onProgress : null
  const total = file.size
  if (total <= CHUNK_THRESHOLD) {
    if (onProgress) onProgress({ percent: 0, loaded: 0, total })
    const res = await uploadSiteAsset(siteId, file, opts)
    if (onProgress) onProgress({ percent: 100, loaded: total, total })
    return res
  }

  const fp = fileFingerprint(file)
  const key = storageKey(siteId)
  let uploadId = null
  let chunkSize = DEFAULT_CHUNK_SIZE
  let totalChunks = 0
  const received = new Set()

  let cached = null
  try {
    cached = JSON.parse(sessionStorage.getItem(key) || 'null')
  } catch (_) {
    cached = null
  }

  if (cached && cached.fingerprint === fp && cached.upload_id) {
    try {
      const st = await getMultipartUploadStatus(siteId, cached.upload_id)
      if (
        st.total_size === file.size &&
        st.original_filename === file.name &&
        typeof st.chunk_size === 'number' &&
        st.chunk_size > 0
      ) {
        uploadId = cached.upload_id
        chunkSize = st.chunk_size
        totalChunks = st.total_chunks
        for (const i of st.received_chunks || []) {
          received.add(i)
        }
      }
    } catch (_) {
      uploadId = null
      received.clear()
    }
  }

  if (!uploadId) {
    const init = await initMultipartUpload(siteId, {
      filename: file.name,
      total_size: file.size,
      chunk_size: DEFAULT_CHUNK_SIZE,
      folder: opts.folder || '',
      downloadable: Boolean(opts.downloadable),
      preserve_filename: Boolean(opts.preserveFilename)
    })
    uploadId = init.upload_id
    chunkSize = init.chunk_size || DEFAULT_CHUNK_SIZE
    totalChunks = init.total_chunks
    sessionStorage.setItem(key, JSON.stringify({ fingerprint: fp, upload_id: uploadId, total_chunks: totalChunks }))
  }

  const missing = []
  for (let i = 0; i < totalChunks; i++) {
    if (!received.has(i)) missing.push(i)
  }

  const chunkByteLength = (idx) => {
    const start = idx * chunkSize
    const end = Math.min(start + chunkSize, file.size)
    return end - start
  }

  let uploadedBytes = 0
  for (let i = 0; i < totalChunks; i++) {
    if (received.has(i)) uploadedBytes += chunkByteLength(i)
  }

  const reportProgress = () => {
    if (!onProgress) return
    const pct = total <= 0 ? 100 : Math.min(99, Math.round((uploadedBytes / total) * 100))
    onProgress({ percent: pct, loaded: uploadedBytes, total })
  }
  reportProgress()

  const queue = [...missing]
  const worker = async () => {
    while (queue.length) {
      const idx = queue.shift()
      if (idx === undefined) break
      const start = idx * chunkSize
      const end = Math.min(start + chunkSize, file.size)
      const blob = file.slice(start, end)
      await putMultipartChunk(siteId, uploadId, idx, blob)
      uploadedBytes += end - start
      reportProgress()
    }
  }
  await Promise.all(Array.from({ length: Math.min(UPLOAD_CONCURRENCY, Math.max(1, missing.length)) }, () => worker()))

  const done = await completeMultipartUpload(siteId, uploadId)
  try {
    sessionStorage.removeItem(key)
  } catch (_) {}
  if (onProgress) onProgress({ percent: 100, loaded: total, total })
  return done
}

/** 放弃当前站点的分片会话（可选） */
export async function abortSiteAssetResumable(siteId) {
  const key = storageKey(siteId)
  let cached = null
  try {
    cached = JSON.parse(sessionStorage.getItem(key) || 'null')
  } catch (_) {
    cached = null
  }
  if (!cached?.upload_id) return
  try {
    await abortMultipartUpload(siteId, cached.upload_id)
  } catch (_) {}
  try {
    sessionStorage.removeItem(key)
  } catch (_) {}
}
