import type { Ref } from "vue"
import { computed } from "vue"
import type { ContestWork } from "@/api/contests"

/** 单页预览：图片、故事文案、配音（与后端 UGC 快照字段对齐） */
export interface ContestWorkPreviewPage {
  imageUrl: string
  text?: string
  /** 单页配音 URL（后端 files 快照中的 audioUrl），无则省略 */
  audioUrl?: string
  pageNo?: number
}

export interface ContestWorkPreviewResult {
  pages: ContestWorkPreviewPage[]
  /** 从 files 中解析出的 3D 等模型资源 URL（已规范化），顺序与出现顺序一致 */
  modelFileUrls: string[]
}

/** 将 OSS 相对路径等规范化为可展示的绝对 URL（与 ReviewWorkModal 行为一致） */
export function normalizeContestWorkAssetUrl(fileUrl: string): string {
  if (!fileUrl) return ""
  if (fileUrl.startsWith("http://") || fileUrl.startsWith("https://")) {
    return fileUrl
  }
  const baseURL = import.meta.env.VITE_API_BASE_URL || ""
  if (fileUrl.startsWith("/api") && baseURL.includes("/api")) {
    const urlWithoutApi = baseURL.replace(/\/api$/, "")
    return `${urlWithoutApi}${fileUrl}`
  }
  return `${baseURL}${fileUrl.startsWith("/") ? "" : "/"}${fileUrl}`
}

function parseJsonArray<T>(raw: unknown): T[] {
  if (raw == null) return []
  if (typeof raw === "string") {
    try {
      const p = JSON.parse(raw)
      return Array.isArray(p) ? p : []
    } catch {
      return []
    }
  }
  return Array.isArray(raw) ? raw : []
}

/** 是否为 3D 模型等资源（与 WorkDetailModal 扩展名约定一致） */
export function isContestWorkModelFile(urlOrFileName: string): boolean {
  const pathWithoutQuery = urlOrFileName.split("?")[0]
  return /\.(glb|gltf|obj|fbx|stl|zip)$/i.test(pathWithoutQuery)
}

function isImageFileName(url: string): boolean {
  const pathWithoutQuery = url.split("?")[0]
  return /\.(jpg|jpeg|png|gif|webp)$/i.test(pathWithoutQuery)
}

function firstImageAttachmentUrl(work: ContestWork): string {
  const att = work.attachments?.find(
    (a) =>
      a.fileType?.startsWith("image/") ||
      /\.(jpg|jpeg|png|gif|webp)$/i.test(a.fileName || "")
  )
  return att?.fileUrl ? normalizeContestWorkAssetUrl(att.fileUrl) : ""
}

function collectModelUrlsFromFiles(rawFiles: unknown[]): string[] {
  const out: string[] = []
  for (const item of rawFiles) {
    if (typeof item === "string") {
      if (isContestWorkModelFile(item)) {
        out.push(normalizeContestWorkAssetUrl(item))
      }
      continue
    }
    if (typeof item === "object" && item !== null) {
      const o = item as Record<string, unknown>
      const fu = typeof o.fileUrl === "string" ? o.fileUrl : ""
      if (fu && isContestWorkModelFile(fu)) {
        out.push(normalizeContestWorkAssetUrl(fu))
      }
    }
  }
  return out
}

function readPageMetaFromFileEntry(fileAtIndex: unknown): {
  text?: string
  audioUrl?: string
  pageNo?: number
} {
  if (typeof fileAtIndex !== "object" || fileAtIndex === null) {
    return {}
  }
  const o = fileAtIndex as Record<string, unknown>
  const text = typeof o.text === "string" && o.text.trim() ? o.text : undefined
  const audioUrl =
    typeof o.audioUrl === "string" && o.audioUrl.trim()
      ? normalizeContestWorkAssetUrl(o.audioUrl)
      : undefined
  const pageNo =
    typeof o.pageNo === "number"
      ? o.pageNo
      : typeof o.pageNo === "string" && o.pageNo.trim()
        ? Number(o.pageNo)
        : undefined
  return { text, audioUrl, pageNo }
}

function findFileMetaByPageNo(
  rawFiles: unknown[],
  pageNo1: number
): { text?: string; audioUrl?: string } {
  for (const f of rawFiles) {
    if (typeof f !== "object" || f === null) continue
    const o = f as Record<string, unknown>
    const pn = o.pageNo
    const n = typeof pn === "number" ? pn : typeof pn === "string" ? Number(pn) : NaN
    if (n === pageNo1) {
      const text = typeof o.text === "string" && o.text.trim() ? o.text : undefined
      const audioUrl =
        typeof o.audioUrl === "string" && o.audioUrl.trim()
          ? normalizeContestWorkAssetUrl(o.audioUrl)
          : undefined
      return { text, audioUrl }
    }
  }
  return {}
}

/**
 * 解析赛事作品的多页预览：合并 previewUrls 与结构化 files（pageNo / imageUrl / text / audioUrl）。
 * 单图、仅附件、仅 3D 等场景通过 pages 数量与 modelFileUrls 组合表达。
 */
export function buildContestWorkPreviewPages(
  work: ContestWork | null | undefined
): ContestWorkPreviewResult {
  const empty: ContestWorkPreviewResult = { pages: [], modelFileUrls: [] }
  if (!work) return empty

  const rawFiles = parseJsonArray<unknown>(work.files as unknown)
  const modelFileUrls = collectModelUrlsFromFiles(rawFiles)

  let previewUrls = parseJsonArray<string>(work.previewUrls as unknown).map((u) =>
    String(u).trim()
  )
  if (previewUrls.length === 0 && work.previewUrl) {
    previewUrls = [work.previewUrl]
  }
  previewUrls = previewUrls.filter(Boolean).map((u) => normalizeContestWorkAssetUrl(u))

  if (previewUrls.length > 0) {
    const pages: ContestWorkPreviewPage[] = previewUrls.map((imageUrl, i) => {
      const fromIndex = readPageMetaFromFileEntry(rawFiles[i])
      let text = fromIndex.text
      let audioUrl = fromIndex.audioUrl
      let pageNo = fromIndex.pageNo ?? i + 1

      if (!text || !audioUrl) {
        const byNo = findFileMetaByPageNo(rawFiles, i + 1)
        if (!text && byNo.text) text = byNo.text
        if (!audioUrl && byNo.audioUrl) audioUrl = byNo.audioUrl
      }

      return { imageUrl, text, audioUrl, pageNo }
    })
    return { pages, modelFileUrls }
  }

  const structured = rawFiles.filter((f): f is Record<string, unknown> => {
    if (typeof f !== "object" || f === null) return false
    const o = f as Record<string, unknown>
    return typeof o.imageUrl === "string" && String(o.imageUrl).trim() !== ""
  })

  if (structured.length > 0) {
    const sorted = [...structured].sort((a, b) => {
      const pa = Number(a.pageNo) || 0
      const pb = Number(b.pageNo) || 0
      return pa - pb
    })
    const pages: ContestWorkPreviewPage[] = sorted.map((o) => {
      const imageUrl = normalizeContestWorkAssetUrl(String(o.imageUrl))
      const text = typeof o.text === "string" && o.text.trim() ? o.text : undefined
      const audioUrl =
        typeof o.audioUrl === "string" && o.audioUrl.trim()
          ? normalizeContestWorkAssetUrl(o.audioUrl)
          : undefined
      const pageNo =
        typeof o.pageNo === "number"
          ? o.pageNo
          : typeof o.pageNo === "string"
            ? Number(o.pageNo) || undefined
            : undefined
      return { imageUrl, text, audioUrl, pageNo }
    })
    return { pages, modelFileUrls }
  }

  for (const s of rawFiles) {
    if (typeof s === "string" && isImageFileName(s)) {
      return {
        pages: [{ imageUrl: normalizeContestWorkAssetUrl(s) }],
        modelFileUrls,
      }
    }
  }

  if (work.previewUrl) {
    return {
      pages: [{ imageUrl: normalizeContestWorkAssetUrl(work.previewUrl) }],
      modelFileUrls,
    }
  }

  const firstImg = firstImageAttachmentUrl(work)
  if (firstImg) {
    return { pages: [{ imageUrl: firstImg }], modelFileUrls }
  }

  return { pages: [], modelFileUrls }
}

export function useContestWorkPreviewPages(workRef: Ref<ContestWork | null | undefined>) {
  const previewResult = computed(() => buildContestWorkPreviewPages(workRef.value))
  const pages = computed(() => previewResult.value.pages)
  const modelFileUrls = computed(() => previewResult.value.modelFileUrls)
  return { pages, modelFileUrls, previewResult }
}
