export function inferAttachmentMimeType(fileName: string, declaredType?: string): string {
  const safeDeclaredType = typeof declaredType === "string" ? declaredType.trim() : ""
  if (safeDeclaredType && safeDeclaredType !== "application/octet-stream") {
    return safeDeclaredType
  }

  const extension = fileName.toLowerCase().split(".").pop() || ""
  const extensionToMime: Record<string, string> = {
    md: "text/plain",
    markdown: "text/plain",
    txt: "text/plain",
    log: "text/plain",
    json: "application/json",
    csv: "text/csv",
    yml: "text/plain",
    yaml: "text/plain",
    html: "text/html",
    htm: "text/html",
    xml: "application/xml",
    js: "text/javascript",
    mjs: "text/javascript",
    cjs: "text/javascript",
    ts: "text/plain",
    tsx: "text/plain",
    jsx: "text/plain",
    swift: "text/plain",
    m: "text/plain",
    mm: "text/plain",
    pbxproj: "text/plain",
    xcconfig: "text/plain",
    strings: "text/plain",
    plist: "application/xml",
    stringsdict: "application/xml",
    entitlements: "application/xml",
    storyboard: "application/xml",
    xib: "application/xml",
    metal: "text/plain",
    css: "text/css",
    png: "image/png",
    jpg: "image/jpeg",
    jpeg: "image/jpeg",
    gif: "image/gif",
    webp: "image/webp",
    svg: "image/svg+xml",
    pdf: "application/pdf",
  }

  return extensionToMime[extension] || "application/octet-stream"
}

export function isMarkdownAttachment(fileName: string, mimeType?: string): boolean {
  const extension = fileName.toLowerCase().split(".").pop() || ""
  if (extension === "md" || extension === "markdown") return true
  return (mimeType || "").toLowerCase() === "text/markdown"
}

export function isTextPreviewAttachment(fileName: string, mimeType?: string): boolean {
  const extension = fileName.toLowerCase().split(".").pop() || ""
  const textLikeExtensions = new Set([
    "txt",
    "log",
    "json",
    "csv",
    "xml",
    "yml",
    "yaml",
    "ini",
    "cfg",
    "conf",
    "env",
    "toml",
    "properties",
    "sql",
    "sh",
    "bash",
    "zsh",
    "fish",
    "ps1",
    "py",
    "rb",
    "php",
    "go",
    "rs",
    "java",
    "kt",
    "swift",
    "pbxproj",
    "plist",
    "strings",
    "stringsdict",
    "xcconfig",
    "entitlements",
    "storyboard",
    "xib",
    "m",
    "mm",
    "metal",
    "c",
    "h",
    "cpp",
    "hpp",
    "cs",
    "js",
    "mjs",
    "cjs",
    "ts",
    "tsx",
    "jsx",
    "css",
    "scss",
    "sass",
    "less",
    "vue",
    "svelte",
    "html",
    "htm",
  ])

  const safeMimeType = (mimeType || "").toLowerCase()
  if (textLikeExtensions.has(extension)) return true
  if (safeMimeType.startsWith("text/")) return true
  if (safeMimeType === "application/json" || safeMimeType === "application/xml") return true

  return false
}

export function coerceDataUrlMimeType(dataUrl: string, mimeType: string): string {
  if (!dataUrl.startsWith("data:")) return dataUrl

  const commaIndex = dataUrl.indexOf(",")
  if (commaIndex === -1) return dataUrl

  const meta = dataUrl.slice(0, commaIndex)
  const payload = dataUrl.slice(commaIndex + 1)
  const hasBase64 = meta.includes(";base64")
  const encodingSuffix = hasBase64 ? ";base64" : ""

  return `data:${mimeType}${encodingSuffix},${payload}`
}

export async function blobFromDataUrl(dataUrl: string, mimeTypeOverride?: string): Promise<Blob> {
  const response = await fetch(dataUrl)
  const blob = await response.blob()
  if (!mimeTypeOverride || blob.type === mimeTypeOverride) return blob
  const buffer = await blob.arrayBuffer()
  return new Blob([buffer], { type: mimeTypeOverride })
}

export async function textFromDataUrl(dataUrl: string): Promise<string> {
  const response = await fetch(dataUrl)
  return response.text()
}

function safeScriptString(value: string): string {
  return JSON.stringify(value).replace(/<\/script/gi, "<\\/script")
}

function escapeHtml(value: string): string {
  return value
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
}

function getExtension(fileName: string): string {
  return fileName.toLowerCase().split(".").pop() || ""
}

function isJsonAttachment(fileName: string, mimeType?: string): boolean {
  const extension = getExtension(fileName)
  return extension === "json" || (mimeType || "").toLowerCase() === "application/json"
}

function isCsvAttachment(fileName: string, mimeType?: string): boolean {
  const extension = getExtension(fileName)
  return extension === "csv" || (mimeType || "").toLowerCase() === "text/csv"
}

function isHtmlAttachment(fileName: string, mimeType?: string): boolean {
  const extension = getExtension(fileName)
  const safeMimeType = (mimeType || "").toLowerCase()
  return extension === "html" || extension === "htm" || safeMimeType === "text/html"
}

function inferCodeLanguage(fileName: string, mimeType?: string): string {
  const extension = getExtension(fileName)
  const safeMimeType = (mimeType || "").toLowerCase()

  const extensionToLanguage: Record<string, string> = {
    swift: "swift",
    m: "objectivec",
    mm: "objectivec",
    h: "objectivec",
    pbxproj: "ini",
    xcconfig: "ini",
    strings: "ini",
    stringsdict: "xml",
    plist: "xml",
    entitlements: "xml",
    storyboard: "xml",
    xib: "xml",
    metal: "cpp",
    json: "json",
    csv: "plaintext",
    yml: "yaml",
    yaml: "yaml",
    xml: "xml",
    html: "xml",
    htm: "xml",
    js: "javascript",
    mjs: "javascript",
    cjs: "javascript",
    ts: "typescript",
    tsx: "typescript",
    jsx: "javascript",
    css: "css",
    scss: "scss",
    sass: "scss",
    less: "less",
    sh: "bash",
    bash: "bash",
    zsh: "bash",
    fish: "bash",
    ps1: "powershell",
    py: "python",
    rb: "ruby",
    php: "php",
    go: "go",
    rs: "rust",
    java: "java",
    kt: "kotlin",
    c: "c",
    cpp: "cpp",
    hpp: "cpp",
    cs: "csharp",
    sql: "sql",
    vue: "xml",
    svelte: "xml",
    md: "markdown",
    markdown: "markdown",
    txt: "plaintext",
    log: "plaintext",
    ini: "ini",
    cfg: "ini",
    conf: "ini",
    env: "ini",
    toml: "ini",
    properties: "ini",
  }

  const fromExtension = extensionToLanguage[extension]
  if (fromExtension) return fromExtension

  if (safeMimeType === "application/json") return "json"
  if (safeMimeType === "application/xml" || safeMimeType === "text/xml") return "xml"
  if (safeMimeType === "text/html") return "xml"
  if (safeMimeType === "text/css") return "css"
  if (safeMimeType === "text/javascript") return "javascript"

  return "plaintext"
}

function parseCsvLine(line: string): string[] {
  const cells: string[] = []
  let current = ""
  let index = 0
  let inQuotes = false

  while (index < line.length) {
    const char = line[index]
    if (char === '"') {
      const next = line[index + 1]
      if (inQuotes && next === '"') {
        current += '"'
        index += 2
        continue
      }
      inQuotes = !inQuotes
      index += 1
      continue
    }

    if (char === "," && !inQuotes) {
      cells.push(current)
      current = ""
      index += 1
      continue
    }

    current += char
    index += 1
  }

  cells.push(current)
  return cells
}

function csvTableHtml(content: string): string {
  const lines = content.split(/\r?\n/).filter((line) => line.trim().length > 0).slice(0, 200)
  if (lines.length === 0) return '<p class="muted">No CSV rows to preview.</p>'

  const rows = lines.map(parseCsvLine)
  const maxColumns = rows.reduce((max, row) => Math.max(max, row.length), 0)
  const normalizedRows = rows.map((row) => [...row, ...Array.from({ length: maxColumns - row.length }, () => "")])
  const header = normalizedRows[0]
  const body = normalizedRows.slice(1)

  const headerCells = header.map((cell) => `<th>${escapeHtml(cell || "Column")}</th>`).join("")
  const bodyRows = body
    .map((row) => `<tr>${row.map((cell) => `<td>${escapeHtml(cell)}</td>`).join("")}</tr>`)
    .join("")

  return `<table><thead><tr>${headerCells}</tr></thead><tbody>${bodyRows}</tbody></table>`
}

function prettyJsonOrRaw(content: string): string {
  try {
    return JSON.stringify(JSON.parse(content), null, 2)
  } catch {
    return content
  }
}

export function createTextPreviewHtml(fileName: string, content: string, mimeType?: string): string {
  const safeTitle = escapeHtml(fileName)
  const safeMimeType = (mimeType || "").toLowerCase()
  const isHtml = isHtmlAttachment(fileName, safeMimeType)
  const isJson = isJsonAttachment(fileName, safeMimeType)
  const isCsv = isCsvAttachment(fileName, safeMimeType)
  const language = inferCodeLanguage(fileName, safeMimeType)
  const displayedText = isJson ? prettyJsonOrRaw(content) : content

  const sourceLiteral = safeScriptString(displayedText)
  const htmlLiteral = safeScriptString(content)
  const renderedCsv = isCsv ? csvTableHtml(content) : ""

  return `<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>${safeTitle}</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11.11.1/styles/github-dark.min.css" />
  <style>
    body { margin: 0; background: #0f172a; color: #e2e8f0; font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; }
    .container { max-width: 1100px; margin: 0 auto; padding: 24px; }
    .header { color: #94a3b8; font-size: 13px; margin-bottom: 14px; }
    .panel { border: 1px solid #1e293b; background: #020617; border-radius: 10px; padding: 14px; margin-bottom: 14px; overflow: auto; }
    .panel-toolbar { display: flex; align-items: center; justify-content: space-between; margin: 6px 0 8px; }
    .copy-btn { border: 1px solid #334155; background: #111827; color: #e2e8f0; border-radius: 7px; font-size: 12px; padding: 4px 8px; cursor: pointer; }
    .copy-btn:hover { border-color: #475569; }
    .source-meta { color: #64748b; font-size: 12px; }
    .code { margin: 0; white-space: pre-wrap; word-break: break-word; font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, monospace; font-size: 13px; line-height: 1.55; }
    .hljs { background: #020617 !important; border-radius: 8px; padding: 0 !important; }
    .muted { color: #94a3b8; font-size: 13px; }
    table { border-collapse: collapse; width: 100%; font-size: 13px; }
    th, td { border: 1px solid #334155; padding: 6px 8px; text-align: left; vertical-align: top; }
    th { background: #111827; color: #f8fafc; position: sticky; top: 0; }
    .iframe-wrap { border: 1px solid #1e293b; border-radius: 10px; overflow: hidden; background: #ffffff; }
    iframe { display: block; width: 100%; min-height: 360px; border: 0; background: #fff; }
    details { border: 1px solid #1e293b; border-radius: 10px; background: #020617; padding: 10px 12px; }
    summary { cursor: pointer; color: #cbd5e1; font-size: 13px; }
  </style>
</head>
<body>
  <main class="container">
    <div class="header">Attachment Preview: ${safeTitle}${safeMimeType ? ` · ${escapeHtml(safeMimeType)}` : ""}</div>
    ${isHtml ? `<div class="iframe-wrap"><iframe sandbox="" id="html-frame"></iframe></div>` : ""}
    ${isCsv ? `<div class="panel">${renderedCsv}</div>` : ""}
    ${isHtml ? '<details><summary>View source</summary>' : ""}
      <div class="panel-toolbar">
        <button id="copy-source" type="button" class="copy-btn">Copy source</button>
        <span id="source-meta" class="source-meta"></span>
      </div>
      <div class="panel">
        <pre class="code"><code id="source-code" class="language-${language}"></code></pre>
      </div>
    ${isHtml ? '</details>' : ""}
  </main>
  <script src="https://cdn.jsdelivr.net/npm/highlight.js@11.11.1/lib/common.min.js"></script>
  <script>
    const sourceText = ${sourceLiteral};
    const rawHtml = ${htmlLiteral};
    const sourceCodeElement = document.getElementById("source-code");
    if (sourceCodeElement) sourceCodeElement.textContent = sourceText;
    const sourceMetaElement = document.getElementById("source-meta");
    if (sourceMetaElement) {
      const lineCount = sourceText.length === 0 ? 0 : sourceText.split(/\\r?\\n/).length;
      sourceMetaElement.textContent = "${escapeHtml(language)} · " + lineCount + " lines";
    }
    if (window.hljs && sourceCodeElement) {
      try {
        window.hljs.highlightElement(sourceCodeElement);
      } catch (_error) {
        // no-op fallback to plain text
      }
    }
    const copyButton = document.getElementById("copy-source");
    if (copyButton && navigator.clipboard && navigator.clipboard.writeText) {
      copyButton.addEventListener("click", async () => {
        const originalLabel = copyButton.textContent || "Copy source";
        try {
          await navigator.clipboard.writeText(sourceText);
          copyButton.textContent = "Copied";
          setTimeout(() => { copyButton.textContent = originalLabel; }, 1200);
        } catch (_error) {
          copyButton.textContent = "Copy failed";
          setTimeout(() => { copyButton.textContent = originalLabel; }, 1500);
        }
      });
    }
    const htmlFrame = document.getElementById("html-frame");
    if (htmlFrame) htmlFrame.srcdoc = rawHtml;
  </script>
</body>
</html>`
}

export function createMarkdownPreviewHtml(fileName: string, markdown: string): string {
  const safeTitle = fileName.replace(/[<>&"]/g, (char) => {
    if (char === "<") return "&lt;"
    if (char === ">") return "&gt;"
    if (char === "&") return "&amp;"
    return "&quot;"
  })
  const markdownLiteral = safeScriptString(markdown)

  return `<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>${safeTitle}</title>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@11.11.1/styles/github-dark.min.css" />
  <style>
    body { margin: 0; background: #0f172a; color: #e2e8f0; font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif; }
    .container { max-width: 900px; margin: 0 auto; padding: 24px; }
    .header { margin-bottom: 16px; color: #94a3b8; font-size: 14px; }
    .markdown { line-height: 1.7; color: #e2e8f0; }
    .markdown h1, .markdown h2, .markdown h3, .markdown h4 { color: #f8fafc; margin-top: 1.3em; margin-bottom: 0.4em; }
    .markdown pre { background: #020617; border: 1px solid #1e293b; border-radius: 8px; padding: 12px; overflow: auto; }
    .markdown code { background: #020617; border: 1px solid #1e293b; border-radius: 6px; padding: 0.1em 0.35em; }
    .markdown pre code { background: transparent; border: 0; padding: 0; }
    .markdown a { color: #60a5fa; }
    .markdown blockquote { border-left: 3px solid #334155; margin: 0.8em 0; padding-left: 12px; color: #cbd5e1; }
    .markdown table { border-collapse: collapse; width: 100%; }
    .markdown th, .markdown td { border: 1px solid #334155; padding: 6px 8px; }
    .markdown hr { border: 0; border-top: 1px solid #334155; margin: 1.5em 0; }
    .fallback { white-space: pre-wrap; background: #020617; border: 1px solid #1e293b; border-radius: 8px; padding: 12px; color: #e2e8f0; }
  </style>
</head>
<body>
  <main class="container">
    <div class="header">Markdown Preview: ${safeTitle}</div>
    <article id="root" class="markdown"></article>
  </main>
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/highlight.js@11.11.1/lib/common.min.js"></script>
  <script>
    const rawMarkdown = ${markdownLiteral};
    const markdown = rawMarkdown.replace(/</g, "&lt;").replace(/>/g, "&gt;");
    const root = document.getElementById("root");
    const fallback = () => {
      const pre = document.createElement("pre");
      pre.className = "fallback";
      pre.textContent = rawMarkdown;
      root.innerHTML = "";
      root.appendChild(pre);
    };
    try {
      if (window.marked && typeof window.marked.parse === "function") {
        root.innerHTML = window.marked.parse(markdown);
        if (window.hljs) {
          root.querySelectorAll("pre code").forEach((block) => {
            try {
              window.hljs.highlightElement(block);
            } catch (_error) {
              // no-op fallback to plain rendered code block
            }
          });
        }
      } else {
        fallback();
      }
    } catch (_error) {
      fallback();
    }
  </script>
</body>
</html>`;
}

export async function markdownPreviewObjectUrl(fileName: string, dataUrl: string): Promise<string> {
  const markdown = await textFromDataUrl(dataUrl)
  const html = createMarkdownPreviewHtml(fileName, markdown)
  return URL.createObjectURL(new Blob([html], { type: "text/html" }))
}

export async function textPreviewObjectUrl(fileName: string, dataUrl: string, mimeType?: string): Promise<string> {
  const content = await textFromDataUrl(dataUrl)
  const html = createTextPreviewHtml(fileName, content, mimeType)
  return URL.createObjectURL(new Blob([html], { type: "text/html" }))
}
