import { apiClient } from "@/lib/api/client"
import { displayFileName, fileNameExtension } from "@/lib/drive/display-file-name"
import { decodePathSegment } from "@/lib/drive/drive-url"

const EXT_MIME: Record<string, string> = {
  jpg: "image/jpeg",
  jpeg: "image/jpeg",
  png: "image/png",
  gif: "image/gif",
  webp: "image/webp",
  svg: "image/svg+xml",
  bmp: "image/bmp",
  avif: "image/avif",
  heic: "image/heic",
  heif: "image/heif",
  mp4: "video/mp4",
  webm: "video/webm",
  mov: "video/quicktime",
  mkv: "video/x-matroska",
  ogv: "video/ogg",
  m4v: "video/mp4",
  pdf: "application/pdf",
  mp3: "audio/mpeg",
  wav: "audio/wav",
  ogg: "audio/ogg",
  flac: "audio/flac",
  m4a: "audio/mp4",
  aac: "audio/aac",
  opus: "audio/opus",
  weba: "audio/webm",
  aiff: "audio/aiff",
  mid: "audio/midi",
  midi: "audio/midi",
  ico: "image/x-icon",
  tif: "image/tiff",
  tiff: "image/tiff",
  apng: "image/apng",
  jfif: "image/jpeg",
}

function mimeFromFileName(name: string): string | undefined {
  const ext = name.split(".").pop()?.toLowerCase() ?? ""
  return EXT_MIME[ext]
}

const AMBIGUOUS_SERVER_MIMES = new Set([
  "application/octet-stream",
  "binary/octet-stream",
  "text/plain",
  "text/xml",
  "application/xml",
])

function normalizeMime(value: string): string {
  return value.toLowerCase().split(";")[0]?.trim() ?? ""
}

/** Pick MIME browsers accept for <img>/<video>/<audio> from server hints + extension. */
export function resolvePreviewMime(blobType: string, mimeType: string, fileName: string): string {
  const fromExt = mimeFromFileName(fileName)
  const blob = normalizeMime(blobType)
  const hinted = normalizeMime(mimeType)

  if (
    fromExt === "image/svg+xml" ||
    hinted.includes("svg") ||
    blob.includes("svg") ||
    fileName.toLowerCase().endsWith(".svg")
  ) {
    return "image/svg+xml"
  }

  if (fromExt && (AMBIGUOUS_SERVER_MIMES.has(blob) || AMBIGUOUS_SERVER_MIMES.has(hinted) || !blob)) {
    return fromExt
  }

  if (blob && !AMBIGUOUS_SERVER_MIMES.has(blob)) return blob
  if (hinted && !AMBIGUOUS_SERVER_MIMES.has(hinted)) return hinted
  return fromExt ?? blob ?? hinted ?? "application/octet-stream"
}

/** Nextcloud often returns application/octet-stream — browsers need a concrete type for <img>/<video>. */
export function blobForPreview(blob: Blob, mimeType: string, fileName: string): Blob {
  const type = resolvePreviewMime(blob.type, mimeType, fileName)
  if (!type || blob.type === type) return blob
  return new Blob([blob], { type })
}

export async function fetchDrivePreviewBlob(file: {
  path: string
  name: string
  mime_type: string
}): Promise<Blob> {
  const raw = await apiClient.getBlob(driveDownloadApiPath(logicalDriveFilePath(file)))
  return blobForPreview(raw, file.mime_type, file.name)
}

/** Logical client path for download (decode then re-encode; join name if path is parent dir). */
export function logicalDriveFilePath(file: { path: string; name: string }): string {
  const decodeSeg = (seg: string) => {
    try {
      return decodeURIComponent(seg)
    } catch {
      return seg
    }
  }
  let p = file.path.trim()
  if (!p.startsWith("/")) p = `/${p}`
  const segments = p.split("/").filter(Boolean).map(decodeSeg)
  p = segments.length ? `/${segments.join("/")}` : "/"
  const name = displayFileName(file.name)
  if (!name) return p.replace(/\/+/g, "/")

  const base = segments[segments.length - 1] ?? ""
  const baseDecoded = displayFileName(base)
  if (baseDecoded === name) return p.replace(/\/+/g, "/")

  // WebDAV href basename is canonical; don't append a diverging display name.
  const ext = fileNameExtension(name)
  if (ext && baseDecoded.toLowerCase().endsWith(`.${ext}`)) {
    return p.replace(/\/+/g, "/")
  }

  return `${p.replace(/\/$/, "")}/${name}`.replace(/\/+/g, "/")
}

/** API path for GET /drive/filter-corpus/* (recursive file index for client filters). */
export function driveFilterCorpusApiPath(folderPath: string): string {
  if (folderPath === "/" || folderPath.trim() === "") return "/drive/filter-corpus"
  const parts = folderPath
    .replace(/^\/+/, "")
    .split("/")
    .filter(Boolean)
    .map((seg) => encodeURIComponent(decodePathSegment(seg)))
  return `/drive/filter-corpus/${parts.join("/")}`
}

/** API path for GET /drive/files/* (segments URL-encoded). */
export function driveFilesListApiPath(folderPath: string): string {
  if (folderPath === "/" || folderPath.trim() === "") return "/drive/files/"
  const parts = folderPath
    .replace(/^\/+/, "")
    .split("/")
    .filter(Boolean)
    .map((seg) => encodeURIComponent(decodePathSegment(seg)))
  return `/drive/files/${parts.join("/")}`
}

/** API path for GET /drive/preview/* (segments URL-encoded). */
export function drivePreviewApiPath(filePath: string, width = 400, height = 300): string {
  const parts = filePath
    .replace(/^\/+/, "")
    .split("/")
    .filter(Boolean)
    .map((seg) => encodeURIComponent(seg))
  return `/drive/preview/${parts.join("/")}?w=${width}&h=${height}`
}

export async function fetchDriveServerPreview(
  file: { path: string; name: string },
  width = 400,
  height = 300
): Promise<Blob> {
  const logical = logicalDriveFilePath(file)
  return apiClient.getBlob(drivePreviewApiPath(logical, width, height))
}

/** API path for GET /drive/download/* (segments URL-encoded). */
export function driveDownloadApiPath(filePath: string): string {
  const parts = filePath
    .replace(/^\/+/, "")
    .split("/")
    .filter(Boolean)
    .map((seg) => encodeURIComponent(seg))
  return `/drive/download/${parts.join("/")}`
}

export async function downloadDriveFile(
  filePath: string,
  suggestedName?: string,
  fileNameForPath?: string
) {
  const logical =
    fileNameForPath != null
      ? logicalDriveFilePath({ path: filePath, name: fileNameForPath })
      : filePath
  const blob = await apiClient.getBlob(driveDownloadApiPath(logical))
  const name =
    displayFileName(suggestedName ?? filePath.split("/").filter(Boolean).pop() ?? "download")
  const url = URL.createObjectURL(blob)
  try {
    const anchor = document.createElement("a")
    anchor.href = url
    anchor.download = name
    anchor.rel = "noopener"
    document.body.appendChild(anchor)
    anchor.click()
    anchor.remove()
  } finally {
    URL.revokeObjectURL(url)
  }
}

/** Open file in a new tab (preview) with Bearer auth — avoids raw API URL in the browser. */
export async function openDriveFileInNewTab(filePath: string) {
  const blob = await apiClient.getBlob(driveDownloadApiPath(filePath))
  const url = URL.createObjectURL(blob)
  const opened = window.open(url, "_blank", "noopener,noreferrer")
  if (!opened) {
    URL.revokeObjectURL(url)
    await downloadDriveFile(filePath)
    return
  }
  window.setTimeout(() => URL.revokeObjectURL(url), 60_000)
}
