"use client"

import { useState, useRef, useEffect, memo, useMemo } from "react"
import {
  ChevronDown,
  ChevronUp,
  Code,
  Bug,
  Search,
  X,
} from "lucide-react"
import {
  Dialog,
  DialogContent,
  DialogHeader,
  DialogTitle,
  DialogTrigger,
} from "@/components/ui/dialog"
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { CodeHighlighter, CODE_STYLE } from "./code-highlighter"
import type { LLMCallDebugData } from "./timeline-types"

interface ContentPart {
  type: "text" | "code"
  content: string
  language?: string
}

const PromptContent = memo(function PromptContent({ content }: { content: string }) {
  const parts = useMemo(() => {
    const result: ContentPart[] = []
    const lines = content.split("\n")
    let inCode = false
    let codeLang = "python"
    let codeLines: string[] = []
    let textLines: string[] = []
    let depth = 0

    for (const line of lines) {
      if (!inCode) {
        const fence = line.match(/^```(\w+)/)
        if (fence) {
          // Save any pending text
          if (textLines.length > 0) {
            result.push({ type: "text", content: textLines.join("\n") })
            textLines = []
          }
          // Start code block
          inCode = true
          depth = 0
          codeLang = fence[1] || "python"
          codeLines = []
        } else {
          textLines.push(line)
        }
      } else if (line.match(/^```\w/)) {
        // Nested code block
        depth++
        codeLines.push(line)
      } else if (line.trim() === "```") {
        if (depth > 0) {
          // End nested code block
          depth--
          codeLines.push(line)
        } else {
          // End main code block
          result.push({
            type: "code",
            content: codeLines.join("\n").trim(),
            language: codeLang,
          })
          codeLines = []
          inCode = false
        }
      } else {
        codeLines.push(line)
      }
    }

    // Handle unclosed blocks
    if (inCode && codeLines.length > 0) {
      result.push({
        type: "code",
        content: codeLines.join("\n").trim(),
        language: codeLang,
      })
    }
    if (textLines.length > 0) {
      result.push({ type: "text", content: textLines.join("\n") })
    }

    return result.length > 0 ? result : [{ type: "text" as const, content }]
  }, [content])

  return (
    <div className="space-y-3">
      {parts.map((part, index) => {
        if (part.type === "code") {
          return (
            <div key={index} className="rounded border border-zinc-200 dark:border-zinc-700 overflow-hidden">
              <CodeHighlighter
                language={part.language || "python"}
                code={part.content}
                customStyle={CODE_STYLE}
              />
            </div>
          )
        }

        return (
          <pre
            key={index}
            className="text-sm whitespace-pre-wrap break-words text-zinc-800 dark:text-zinc-200 leading-relaxed font-mono"
          >
            {part.content}
          </pre>
        )
      })}
    </div>
  )
})

function clearSearchHighlights(container: HTMLElement) {
  const marks = container.querySelectorAll("mark[data-search-highlight]")
  marks.forEach(mark => {
    const parent = mark.parentNode
    if (parent) {
      parent.replaceChild(document.createTextNode(mark.textContent || ""), mark)
      parent.normalize()
    }
  })
}

function applySearchHighlights(container: HTMLElement, query: string): number {
  clearSearchHighlights(container)
  if (!query.trim()) return 0

  const lowerQuery = query.toLowerCase()
  const walker = document.createTreeWalker(container, NodeFilter.SHOW_TEXT)
  const nodesToProcess: { node: Text; matches: { start: number; end: number }[] }[] = []

  let textNode: Text | null
  while ((textNode = walker.nextNode() as Text | null)) {
    const text = textNode.textContent || ""
    const lowerText = text.toLowerCase()
    const matches: { start: number; end: number }[] = []
    let searchFrom = 0

    while (true) {
      const index = lowerText.indexOf(lowerQuery, searchFrom)
      if (index === -1) break
      matches.push({ start: index, end: index + query.length })
      searchFrom = index + query.length
    }

    if (matches.length > 0) {
      nodesToProcess.push({ node: textNode, matches })
    }
  }

  let totalMatches = 0

  for (const { node, matches } of nodesToProcess) {
    const text = node.textContent || ""
    const fragment = document.createDocumentFragment()
    let lastIndex = 0

    for (const match of matches) {
      if (match.start > lastIndex) {
        fragment.appendChild(document.createTextNode(text.slice(lastIndex, match.start)))
      }

      const mark = document.createElement("mark")
      mark.setAttribute("data-search-highlight", "")
      mark.setAttribute("data-match-index", String(totalMatches))
      mark.className = "bg-yellow-200 dark:bg-yellow-700/70 text-inherit rounded-[2px]"
      mark.textContent = text.slice(match.start, match.end)
      fragment.appendChild(mark)

      totalMatches++
      lastIndex = match.end
    }

    if (lastIndex < text.length) {
      fragment.appendChild(document.createTextNode(text.slice(lastIndex)))
    }

    node.parentNode?.replaceChild(fragment, node)
  }

  return totalMatches
}

function scrollToSearchMatch(container: HTMLElement, index: number) {
  const marks = container.querySelectorAll("mark[data-search-highlight]")
  marks.forEach(m => {
    (m as HTMLElement).className = "bg-yellow-200 dark:bg-yellow-700/70 text-inherit rounded-[2px]"
  })

  const target = marks[index] as HTMLElement | undefined
  if (target) {
    target.className = "bg-orange-300 dark:bg-orange-600/70 text-inherit rounded-[2px] ring-2 ring-orange-400 dark:ring-orange-500"
    target.scrollIntoView({ behavior: "smooth", block: "center" })
  }
}

interface LLMCallDebugDialogProps {
  debugData: LLMCallDebugData
  title: string
  model?: string | null
}

export const LLMCallDebugDialog = memo(function LLMCallDebugDialog({
  debugData,
  title,
  model,
}: LLMCallDebugDialogProps) {
  const [open, setOpen] = useState(false)
  const [activeTab, setActiveTab] = useState<"user" | "system">("user")
  const [showResponse, setShowResponse] = useState(false)
  const [contentReady, setContentReady] = useState(false)
  const [searchOpen, setSearchOpen] = useState(false)
  const [searchQuery, setSearchQuery] = useState("")
  const [matchCount, setMatchCount] = useState(0)
  const [currentMatch, setCurrentMatch] = useState(-1)
  const contentRef = useRef<HTMLDivElement>(null)
  const searchInputRef = useRef<HTMLInputElement>(null)

  const [fetchedData, setFetchedData] = useState<{
    systemPrompt: string | null
    userPrompt: string | null
    rawResponse: string | null
  } | null>(null)
  const [fetchLoading, setFetchLoading] = useState(false)
  const [fetchError, setFetchError] = useState<string | null>(null)

  useEffect(() => {
    if (!open || fetchedData || !debugData.callId) return

    const controller = new AbortController()
    setFetchLoading(true)
    setFetchError(null)

    fetch(`/api/observability/llm-call-debug?callId=${encodeURIComponent(debugData.callId)}`, {
      signal: controller.signal,
    })
      .then(res => {
        if (!res.ok) throw new Error(`Failed to fetch debug data: ${res.status}`)
        return res.json()
      })
      .then(data => {
        setFetchedData(data)
        setFetchLoading(false)
      })
      .catch(err => {
        if (err.name === "AbortError") return
        setFetchError(err.message)
        setFetchLoading(false)
      })

    return () => controller.abort()
  }, [open, fetchedData, debugData.callId])

  useEffect(() => {
    if (open) {
      const timer = requestAnimationFrame(() => setContentReady(true))
      return () => cancelAnimationFrame(timer)
    } else {
      setContentReady(false)
      setShowResponse(false)
      setSearchOpen(false)
      setSearchQuery("")
      setMatchCount(0)
      setCurrentMatch(-1)
    }
  }, [open])

  useEffect(() => {
    if (searchOpen) {
      requestAnimationFrame(() => searchInputRef.current?.focus())
    }
  }, [searchOpen])

  useEffect(() => {
    const container = contentRef.current
    if (!container || !contentReady) return

    const timer = setTimeout(() => {
      clearSearchHighlights(container)
      if (!searchQuery.trim()) {
        setMatchCount(0)
        setCurrentMatch(-1)
        return
      }
      const count = applySearchHighlights(container, searchQuery)
      setMatchCount(count)
      const next = count > 0 ? 0 : -1
      setCurrentMatch(next)
      if (count > 0) {
        scrollToSearchMatch(container, 0)
      }
    }, 150)

    return () => clearTimeout(timer)
  }, [searchQuery, activeTab, showResponse, contentReady])

  function navigateMatch(direction: "next" | "prev") {
    if (matchCount === 0) return

    const next = direction === "next"
      ? (currentMatch + 1) % matchCount
      : (currentMatch - 1 + matchCount) % matchCount

    setCurrentMatch(next)
    if (contentRef.current) {
      scrollToSearchMatch(contentRef.current, next)
    }
  }

  function closeSearch() {
    setSearchOpen(false)
    setSearchQuery("")
    if (contentRef.current) {
      clearSearchHighlights(contentRef.current)
    }
  }

  const isLoading = fetchLoading || !fetchedData

  const searchBar = searchOpen ? (
    <div className="flex items-center gap-2 px-3 py-2 mb-3 bg-zinc-100 dark:bg-zinc-800 border border-zinc-200 dark:border-zinc-700 rounded-md">
      <Search className="h-3.5 w-3.5 text-zinc-400 flex-shrink-0" />
      <input
        ref={searchInputRef}
        type="text"
        value={searchQuery}
        onChange={e => setSearchQuery(e.target.value)}
        onKeyDown={e => {
          if (e.key === "Enter") {
            e.preventDefault()
            navigateMatch(e.shiftKey ? "prev" : "next")
          } else if (e.key === "Escape") {
            e.preventDefault()
            e.stopPropagation()
            closeSearch()
          }
        }}
        placeholder={`Search ${activeTab === "user" ? "user" : "system"} prompt...`}
        className="flex-1 bg-transparent text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 outline-none min-w-0"
      />
      {searchQuery && (
        <span className="text-xs text-zinc-500 dark:text-zinc-400 whitespace-nowrap">
          {matchCount > 0 ? `${currentMatch + 1} of ${matchCount}` : "No matches"}
        </span>
      )}
      <div className="flex items-center gap-0.5">
        <button
          onClick={() => navigateMatch("prev")}
          disabled={matchCount === 0}
          className="p-1 rounded text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300 hover:bg-zinc-200 dark:hover:bg-zinc-700 disabled:opacity-30 disabled:cursor-default transition-colors"
          title="Previous match (Shift+Enter)"
        >
          <ChevronUp className="h-3.5 w-3.5" />
        </button>
        <button
          onClick={() => navigateMatch("next")}
          disabled={matchCount === 0}
          className="p-1 rounded text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300 hover:bg-zinc-200 dark:hover:bg-zinc-700 disabled:opacity-30 disabled:cursor-default transition-colors"
          title="Next match (Enter)"
        >
          <ChevronDown className="h-3.5 w-3.5" />
        </button>
        <button
          onClick={closeSearch}
          className="p-1 rounded text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300 hover:bg-zinc-200 dark:hover:bg-zinc-700 transition-colors"
          title="Close search (Escape)"
        >
          <X className="h-3.5 w-3.5" />
        </button>
      </div>
    </div>
  ) : (
    <div className="flex justify-end mb-2">
      <button
        onClick={() => setSearchOpen(true)}
        className="p-1.5 rounded text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-700 transition-colors"
        title="Search (Ctrl+F)"
      >
        <Search className="h-4 w-4" />
      </button>
    </div>
  )

  const loadingSkeleton = (
    <div className="animate-pulse space-y-3">
      <div className="h-4 bg-zinc-200 dark:bg-zinc-700 rounded w-3/4" />
      <div className="h-4 bg-zinc-200 dark:bg-zinc-700 rounded w-1/2" />
      <div className="h-4 bg-zinc-200 dark:bg-zinc-700 rounded w-2/3" />
    </div>
  )

  return (
    <Dialog open={open} onOpenChange={setOpen}>
      <DialogTrigger asChild>
        <button
          className="p-1.5 rounded text-zinc-400 hover:text-zinc-600 dark:hover:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-700 transition-colors"
          title="View LLM call details"
        >
          <Bug className="h-4 w-4" />
        </button>
      </DialogTrigger>
      <DialogContent
        className="w-[95vw] max-w-[95vw] h-[90vh] max-h-[90vh] flex flex-col overflow-hidden"
        onKeyDown={(e) => {
          if ((e.metaKey || e.ctrlKey) && e.key === "f") {
            e.preventDefault()
            setSearchOpen(true)
          }
        }}
      >
        <DialogHeader className="flex-shrink-0 pb-3 border-b border-zinc-200 dark:border-zinc-700">
          <div className="flex items-center justify-between pr-6">
            <DialogTitle className="flex items-center gap-2">
              <Bug className="h-4 w-4" />
              <span>{title}</span>
            </DialogTitle>
            <button
              onClick={() => setShowResponse(!showResponse)}
              disabled={isLoading}
              className={`flex items-center gap-1.5 px-2 py-1 text-xs rounded transition-colors duration-150 ${
                showResponse
                  ? "bg-green-100 dark:bg-green-900/50 text-green-700 dark:text-green-300"
                  : "text-zinc-600 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-700"
              } ${isLoading ? "opacity-50 cursor-default" : ""}`}
              title="View raw LLM response"
            >
              <Code className="h-3 w-3" />
              <span>Response</span>
              {fetchedData && (
                <span className="text-xs opacity-70">
                  ({(fetchedData.rawResponse?.length || 0).toLocaleString()})
                </span>
              )}
            </button>
          </div>
          {model && (
            <div className="flex items-center gap-2 mt-2 text-sm">
              <span className="text-xs px-1.5 py-0.5 bg-zinc-100 dark:bg-zinc-700 text-zinc-600 dark:text-zinc-300 rounded">
                {model}
              </span>
            </div>
          )}
        </DialogHeader>

        {fetchError ? (
          <div className="flex-1 flex items-center justify-center">
            <p className="text-sm text-red-500">Failed to load debug data: {fetchError}</p>
          </div>
        ) : isLoading ? (
          <div className="flex-1 flex flex-col min-h-0 mt-3 p-4">
            {loadingSkeleton}
          </div>
        ) : showResponse ? (
          <div className="flex-1 flex flex-col min-h-0 mt-3">
            <div ref={contentRef} className="flex-1 overflow-y-auto p-4 bg-white dark:bg-zinc-900 rounded-sm border border-zinc-200 dark:border-zinc-700">
              {fetchedData.rawResponse ? (
                <pre className="text-sm whitespace-pre-wrap break-words text-zinc-800 dark:text-zinc-200 leading-relaxed font-mono">
                  {fetchedData.rawResponse}
                </pre>
              ) : (
                <span className="text-zinc-400">No response</span>
              )}
            </div>
          </div>
        ) : (
          <Tabs value={activeTab} onValueChange={v => setActiveTab(v as "user" | "system")} className="flex-1 flex flex-col min-h-0 mt-3">
            <TabsList className="flex-shrink-0 w-fit mx-auto">
              <TabsTrigger value="user">
                User Prompt
                <span className="ml-1.5 text-xs text-zinc-400">
                  ({(fetchedData.userPrompt?.length || 0).toLocaleString()} chars)
                </span>
              </TabsTrigger>
              <TabsTrigger value="system">
                System Prompt
                <span className="ml-1.5 text-xs text-zinc-400">
                  ({(fetchedData.systemPrompt?.length || 0).toLocaleString()} chars)
                </span>
              </TabsTrigger>
            </TabsList>

            <div className="flex-1 flex flex-col min-h-0 mt-3 p-4 bg-white dark:bg-zinc-900 rounded-sm border border-zinc-200 dark:border-zinc-700">
              {searchBar}
              <div ref={contentRef} className="flex-1 overflow-y-auto">
                {!contentReady ? (
                  loadingSkeleton
                ) : activeTab === "user" ? (
                  fetchedData.userPrompt ? (
                    <PromptContent content={fetchedData.userPrompt} />
                  ) : (
                    <span className="text-zinc-400">No user prompt</span>
                  )
                ) : (
                  fetchedData.systemPrompt ? (
                    <pre className="text-sm whitespace-pre-wrap break-words text-zinc-800 dark:text-zinc-200 leading-relaxed font-mono">
                      {fetchedData.systemPrompt}
                    </pre>
                  ) : (
                    <span className="text-zinc-400">No system prompt</span>
                  )
                )}
              </div>
            </div>
          </Tabs>
        )}
      </DialogContent>
    </Dialog>
  )
})
