/**
 * Function Block Renderer — Slim version from MCP-SuperAssistant
 * Renders detected function_call blocks into interactive UI (Execute button, params, results)
 */
import { styles } from "./styles"
import {
  containsFunctionCalls,
  extractFunctionName,
  extractJSONParameters,
  extractXMLParameters,
  extractCleanContent,
} from "../parser"
import { CONFIG } from "../config"
import { createLogger } from "../logger"

const logger = createLogger("Renderer")

// ─── State ──────────────────────────────────────────────────────────

let stylesInjected = false
const processedBlocks = new WeakSet<HTMLElement>()

// SVG Icons
const ICONS = {
  BOLT: `<svg width="12" height="12" viewBox="0 0 24 24" fill="currentColor"><path d="M13 2L3 14h9l-1 10 10-12h-9l1-10z"/></svg>`,
  PLAY: `<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M8 5v14l11-7z"/></svg>`,
  CODE: `<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z"/></svg>`,
  SPINNER: `<svg width="16" height="16" viewBox="0 0 50 50"><circle cx="25" cy="25" r="20" fill="none" stroke="currentColor" stroke-width="5" stroke-linecap="round" stroke-dasharray="31.4 31.4" transform="rotate(-90 25 25)"><animateTransform attributeName="transform" type="rotate" from="0 25 25" to="360 25 25" dur="0.8s" repeatCount="indefinite"/></circle></svg>`,
  CHECK: `<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z"/></svg>`,
  ERROR: `<svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-2h2v2zm0-4h-2V7h2v6z"/></svg>`,
}

// ─── Style Injection ────────────────────────────────────────────────

function injectStyles(): void {
  if (stylesInjected || typeof document === "undefined") return
  const el = document.createElement("style")
  el.textContent = styles
  el.setAttribute("data-n8n-superassistant", "true")
  document.head.appendChild(el)
  stylesInjected = true
}

// ─── Render Function Block ──────────────────────────────────────────

/** Render a single function call block into interactive UI */
export function renderFunctionBlock(
  sourceElement: HTMLElement,
  rawContent: string,
  options: { isStreaming?: boolean } = {}
): HTMLDivElement {
  const funcName = extractFunctionName(rawContent) || "unknown_function"
  const isJSON = rawContent.includes('"type"') && rawContent.includes("function_call")
  const params = isJSON ? extractJSONParameters(rawContent) : extractXMLParameters(rawContent)
  const isComplete = !options.isStreaming

  // ── Build block ──
  const block = document.createElement("div")
  block.className = "function-block"
  block.setAttribute("data-function-name", funcName)
  block.setAttribute("data-block-id", `fn-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`)

  // Header
  const header = document.createElement("div")
  header.className = "function-header"
  header.innerHTML = `
    <span class="function-icon">${ICONS.BOLT}</span>
    <span class="function-name">${escapeHtml(funcName)}</span>
    <span class="function-status ${isComplete ? "complete" : "streaming"}">
      ${isComplete ? "Ready" : "Streaming..."}
    </span>
  `
  block.appendChild(header)

  // Parameters
  if (Object.keys(params).length > 0) {
    const paramsDiv = document.createElement("div")
    paramsDiv.className = "function-params"
    for (const [key, value] of Object.entries(params)) {
      const row = document.createElement("div")
      row.className = "param-row"
      row.innerHTML = `
        <span class="param-name">${escapeHtml(key)}</span>
        <div class="param-value">${escapeHtml(String(value))}</div>
      `
      paramsDiv.appendChild(row)
    }
    block.appendChild(paramsDiv)
  }

  // Buttons
  if (isComplete) {
    const buttonsDiv = document.createElement("div")
    buttonsDiv.className = "function-buttons"

    // Execute button
    const execBtn = createExecuteButton(funcName, params, block)
    buttonsDiv.appendChild(execBtn)

    // Raw toggle
    const rawBtn = createRawToggle(rawContent, block)
    buttonsDiv.appendChild(rawBtn)

    block.appendChild(buttonsDiv)
  }

  return block
}

// ─── Execute Button ─────────────────────────────────────────────────

function createExecuteButton(
  funcName: string,
  params: Record<string, any>,
  block: HTMLDivElement
): HTMLButtonElement {
  const btn = document.createElement("button")
  btn.className = "execute-button"
  btn.innerHTML = `${ICONS.PLAY}<span>Run</span>`

  btn.onclick = async () => {
    const textSpan = btn.querySelector("span")!
    btn.disabled = true
    textSpan.style.display = "none"
    const spinner = document.createElement("span")
    spinner.className = "execute-spinner"
    spinner.innerHTML = ICONS.SPINNER
    btn.appendChild(spinner)

    // Create or get results panel
    let resultsPanel = block.querySelector(".function-results-panel") as HTMLDivElement
    if (!resultsPanel) {
      resultsPanel = document.createElement("div")
      resultsPanel.className = "function-results-panel"
      block.appendChild(resultsPanel)
    }
    resultsPanel.style.display = "block"
    resultsPanel.innerHTML = `<div style="color: rgba(255,255,255,0.5); display: flex; align-items: center; gap: 6px;">${ICONS.SPINNER} Executing...</div>`

    try {
      // Use McpService bridge exposed on window
      const mcpClient = (window as any).__n8nMcpBridge
      if (!mcpClient) {
        throw new Error("MCP bridge not initialized. Please connect to MCP server first.")
      }

      const startTime = Date.now()
      const result = await mcpClient.callTool(funcName, params)
      const duration = Date.now() - startTime

      // Success
      resultsPanel.className = "function-results-panel success"
      resultsPanel.innerHTML = `
        <div class="result-header success">${ICONS.CHECK} Success (${duration}ms)</div>
        <div class="result-content">${escapeHtml(formatResult(result))}</div>
      `

      // Update status
      const status = block.querySelector(".function-status")
      if (status) {
        status.className = "function-status complete"
        status.textContent = "Done ✓"
      }

      logger.info(`Tool ${funcName} executed in ${duration}ms`)
    } catch (err: any) {
      const msg = err instanceof Error ? err.message : String(err)
      resultsPanel.className = "function-results-panel error"
      resultsPanel.innerHTML = `
        <div class="result-header error">${ICONS.ERROR} Error</div>
        <div class="result-content">${escapeHtml(msg)}</div>
      `
      logger.error(`Tool ${funcName} failed:`, msg)
    } finally {
      btn.disabled = false
      textSpan.style.display = ""
      if (btn.contains(spinner)) btn.removeChild(spinner)
    }
  }

  return btn
}

// ─── Raw XML/JSON Toggle ────────────────────────────────────────────

function createRawToggle(rawContent: string, block: HTMLDivElement): HTMLButtonElement {
  const btn = document.createElement("button")
  btn.className = "raw-toggle"
  btn.innerHTML = `${ICONS.CODE}<span>Show Raw</span>`

  let rawPanel: HTMLDivElement | null = null
  let isVisible = false

  btn.onclick = () => {
    isVisible = !isVisible
    const textSpan = btn.querySelector("span")!
    textSpan.textContent = isVisible ? "Hide Raw" : "Show Raw"

    if (!rawPanel) {
      rawPanel = document.createElement("div")
      rawPanel.className = "function-results-panel xml-results-panel"
      const pre = document.createElement("pre")
      pre.textContent = extractCleanContent(rawContent)
      pre.style.cssText = "white-space:pre-wrap;margin:0;font-size:11px;line-height:1.5;"
      rawPanel.appendChild(pre)
      block.appendChild(rawPanel)
    }

    rawPanel.style.display = isVisible ? "block" : "none"
  }

  return btn
}

// ─── DOM Observer ───────────────────────────────────────────────────

let observer: MutationObserver | null = null

/** Scan DOM for function call blocks and render them */
export function scanAndRender(): void {
  if (typeof document === "undefined") return
  injectStyles()

  const selectors = CONFIG.targetSelectors.join(", ")
  const elements = document.querySelectorAll<HTMLElement>(selectors)

  elements.forEach((el) => {
    if (processedBlocks.has(el)) return
    if (el.closest(".function-block")) return

    const info = containsFunctionCalls(el)
    if (!info.hasFunctionCalls) return

    processedBlocks.add(el)
    const content = el.textContent?.trim() || ""
    const block = renderFunctionBlock(el, content, {
      isStreaming: !info.isComplete,
    })

    // Replace original element
    el.style.display = "none"
    el.parentNode?.insertBefore(block, el.nextSibling)

    logger.info(`Rendered function block: ${info.invokeName || "unknown"}`)
  })
}

/** Start observing DOM for new function call blocks */
export function startObserver(): void {
  if (observer || typeof document === "undefined") return

  injectStyles()
  scanAndRender() // Initial scan

  observer = new MutationObserver((mutations) => {
    let shouldScan = false
    for (const mutation of mutations) {
      if (mutation.type === "childList" && mutation.addedNodes.length > 0) {
        shouldScan = true
        break
      }
    }
    if (shouldScan) {
      requestAnimationFrame(scanAndRender)
    }
  })

  observer.observe(document.body, {
    childList: true,
    subtree: true,
  })

  logger.info("DOM observer started")
}

/** Stop observing */
export function stopObserver(): void {
  observer?.disconnect()
  observer = null
  logger.info("DOM observer stopped")
}

// ─── Utilities ──────────────────────────────────────────────────────

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

function formatResult(result: any): string {
  if (typeof result === "string") return result
  try {
    return JSON.stringify(result, null, 2)
  } catch {
    return String(result)
  }
}
