'use client'

import { useState, useCallback, useEffect, useRef } from 'react'
import { cn } from '@/lib/utils'
import { 
  Play, 
  ArrowClockwise, 
  Copy, 
  Check, 
  Spinner,
  X,
  FileText,
  BracketsCurly,
  Key,
  CaretRight,
  CaretDown,
  MagnifyingGlass,
  Lightning,
  ArrowsClockwise
} from '@phosphor-icons/react'
import { Button } from '@/components/ui/button'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
import Editor, { loader } from '@monaco-editor/react'

// Configure Monaco to use CDN
loader.config({
  paths: {
    vs: 'https://cdn.jsdelivr.net/npm/monaco-editor@0.45.0/min/vs',
  },
})

// ============================================================================
// Types
// ============================================================================

export interface GraphQLOperationItem {
  id: string
  name: string
  description?: string | null
  operationType: 'query' | 'mutation' | 'subscription'
  query: string
  exampleVariables?: Record<string, unknown>
}

interface GraphQLPlaygroundProps {
  /** GraphQL endpoint URL */
  endpoint: string
  /** Initial query to display */
  defaultQuery?: string
  /** Headers to include with requests */
  headers?: Record<string, string>
  /** Operations from parsed schema */
  operations?: GraphQLOperationItem[]
  /** Selected operation ID (controlled from outside) */
  selectedOperationId?: string
  /** Hide internal schema explorer (when using main sidebar) */
  hideExplorer?: boolean
  /** Custom class name */
  className?: string
  /** Theme */
  theme?: 'light' | 'dark'
}

interface GraphQLResponse {
  data?: unknown
  errors?: Array<{ 
    message: string
    locations?: Array<{ line: number; column: number }>
    path?: Array<string | number>
  }>
  extensions?: Record<string, unknown>
}

type TabType = 'variables' | 'headers'

const STORAGE_KEY = 'brainfish-graphql-playground'

// ============================================================================
// Helpers
// ============================================================================

function loadStoredState(): { query?: string; variables?: string; headers?: string } | null {
  if (typeof window === 'undefined') return null
  try {
    const stored = localStorage.getItem(STORAGE_KEY)
    if (stored) return JSON.parse(stored)
  } catch { /* ignore */ }
  return null
}

function saveStoredState(state: { query: string; variables: string; headers: string }): void {
  if (typeof window === 'undefined') return
  try {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(state))
  } catch { /* ignore */ }
}

// ============================================================================
// Schema Explorer Sidebar
// ============================================================================

interface SchemaExplorerProps {
  operations: GraphQLOperationItem[]
  onSelectOperation: (operation: GraphQLOperationItem) => void
  selectedOperationId?: string
}

function SchemaExplorer({ operations, onSelectOperation, selectedOperationId }: SchemaExplorerProps) {
  const [searchQuery, setSearchQuery] = useState('')
  const [expandedSections, setExpandedSections] = useState<Record<string, boolean>>({
    query: true,
    mutation: true,
    subscription: true,
  })

  // Group operations by type
  const queries = operations.filter(op => op.operationType === 'query')
  const mutations = operations.filter(op => op.operationType === 'mutation')
  const subscriptions = operations.filter(op => op.operationType === 'subscription')

  // Filter by search
  const filterOps = (ops: GraphQLOperationItem[]) => {
    if (!searchQuery.trim()) return ops
    const query = searchQuery.toLowerCase()
    return ops.filter(op => 
      op.name.toLowerCase().includes(query) ||
      op.description?.toLowerCase().includes(query)
    )
  }

  const filteredQueries = filterOps(queries)
  const filteredMutations = filterOps(mutations)
  const filteredSubscriptions = filterOps(subscriptions)

  const toggleSection = (section: string) => {
    setExpandedSections(prev => ({
      ...prev,
      [section]: !prev[section],
    }))
  }

  const renderSection = (
    title: string,
    icon: React.ReactNode,
    ops: GraphQLOperationItem[],
    sectionKey: string,
    iconColor: string
  ) => {
    if (ops.length === 0) return null
    const isExpanded = expandedSections[sectionKey]

    return (
      <div className="mb-2">
        <button
          onClick={() => toggleSection(sectionKey)}
          className="w-full flex items-center gap-2 px-3 py-2 text-xs font-semibold uppercase tracking-wider text-muted-foreground hover:bg-muted/50 transition-colors"
        >
          {isExpanded ? (
            <CaretDown className="w-3 h-3" />
          ) : (
            <CaretRight className="w-3 h-3" />
          )}
          <span className={iconColor}>{icon}</span>
          <span>{title}</span>
          <span className="ml-auto text-[10px] font-normal opacity-60">
            {ops.length}
          </span>
        </button>
        
        {isExpanded && (
          <div className="ml-2">
            {ops.map((op) => (
              <button
                key={op.id}
                onClick={() => onSelectOperation(op)}
                className={cn(
                  'w-full flex items-start gap-2 px-3 py-2 text-left',
                  'hover:bg-muted/50 transition-colors rounded-sm',
                  'border-l-2',
                  selectedOperationId === op.id
                    ? 'bg-muted/70 border-primary'
                    : 'border-transparent'
                )}
              >
                <div className="flex-1 min-w-0">
                  <div className="text-sm font-medium truncate">
                    {op.name}
                  </div>
                  {op.description && (
                    <div className="text-xs text-muted-foreground truncate mt-0.5">
                      {op.description}
                    </div>
                  )}
                </div>
              </button>
            ))}
          </div>
        )}
      </div>
    )
  }

  const hasOperations = operations.length > 0
  const hasFilteredResults = filteredQueries.length > 0 || filteredMutations.length > 0 || filteredSubscriptions.length > 0

  return (
    <div className="flex flex-col h-full border-r border-border bg-muted/20">
      {/* Search */}
      <div className="p-2 border-b border-border">
        <div className="relative">
          <MagnifyingGlass className="absolute left-2.5 top-1/2 -translate-y-1/2 w-4 h-4 text-muted-foreground" />
          <input
            type="text"
            value={searchQuery}
            onChange={(e) => setSearchQuery(e.target.value)}
            placeholder="Search operations..."
            className={cn(
              'w-full pl-8 pr-3 py-1.5 text-sm rounded-md',
              'bg-background border border-border',
              'focus:outline-none focus:ring-1 focus:ring-primary',
              'placeholder:text-muted-foreground'
            )}
          />
        </div>
      </div>

      {/* Operations list */}
      <div className="flex-1 overflow-y-auto py-2">
        {!hasOperations ? (
          <div className="flex flex-col items-center justify-center h-full px-4 text-center">
            <FileText className="w-8 h-8 text-muted-foreground/50 mb-2" />
            <p className="text-sm text-muted-foreground">
              No schema loaded
            </p>
            <p className="text-xs text-muted-foreground/60 mt-1">
              Operations will appear here
            </p>
          </div>
        ) : !hasFilteredResults ? (
          <div className="flex flex-col items-center justify-center h-32 px-4 text-center">
            <p className="text-sm text-muted-foreground">
              No matching operations
            </p>
          </div>
        ) : (
          <>
            {renderSection(
              'Queries',
              <MagnifyingGlass className="w-3.5 h-3.5" weight="bold" />,
              filteredQueries,
              'query',
              'text-blue-500'
            )}
            {renderSection(
              'Mutations',
              <Lightning className="w-3.5 h-3.5" weight="bold" />,
              filteredMutations,
              'mutation',
              'text-orange-500'
            )}
            {renderSection(
              'Subscriptions',
              <ArrowsClockwise className="w-3.5 h-3.5" weight="bold" />,
              filteredSubscriptions,
              'subscription',
              'text-purple-500'
            )}
          </>
        )}
      </div>
    </div>
  )
}

// ============================================================================
// Main Component
// ============================================================================

export function GraphQLPlayground({
  endpoint,
  defaultQuery = `# Welcome to the GraphQL Playground!
#
# Select an operation from the sidebar, or
# type your query below and press Ctrl+Enter

query {
  __typename
}
`,
  headers: defaultHeaders = {},
  operations = [],
  selectedOperationId: externalSelectedId,
  hideExplorer = false,
  className,
  theme = 'dark',
}: GraphQLPlaygroundProps) {
  const [query, setQuery] = useState(defaultQuery)
  const [variables, setVariables] = useState('{}')
  const [customHeaders, setCustomHeaders] = useState(
    Object.keys(defaultHeaders).length > 0 
      ? JSON.stringify(defaultHeaders, null, 2) 
      : '{}'
  )
  const [response, setResponse] = useState<GraphQLResponse | null>(null)
  const [isLoading, setIsLoading] = useState(false)
  const [error, setError] = useState<string | null>(null)
  const [copied, setCopied] = useState(false)
  const [activeTab, setActiveTab] = useState<TabType>('variables')
  const [responseTime, setResponseTime] = useState<number | null>(null)
  const [internalSelectedId, setInternalSelectedId] = useState<string | undefined>()
  
  // Use external selection if provided, otherwise use internal
  const selectedOperationId = externalSelectedId ?? internalSelectedId
  
  const abortControllerRef = useRef<AbortController | null>(null)
  const hasLoadedRef = useRef(false)

  // Load from localStorage on mount
  useEffect(() => {
    const stored = loadStoredState()
    if (stored) {
      if (stored.query) setQuery(stored.query)
      if (stored.variables) setVariables(stored.variables)
      if (stored.headers) setCustomHeaders(stored.headers)
    }
    hasLoadedRef.current = true
  }, [])

  // Save to localStorage when values change
  useEffect(() => {
    if (!hasLoadedRef.current) return
    const timeoutId = setTimeout(() => {
      saveStoredState({ query, variables, headers: customHeaders })
    }, 500)
    return () => clearTimeout(timeoutId)
  }, [query, variables, customHeaders])

  // Handle operation selection from sidebar (internal)
  const handleSelectOperation = useCallback((operation: GraphQLOperationItem) => {
    setQuery(operation.query)
    setInternalSelectedId(operation.id)
    
    // Set example variables if available
    if (operation.exampleVariables && Object.keys(operation.exampleVariables).length > 0) {
      setVariables(JSON.stringify(operation.exampleVariables, null, 2))
    } else {
      setVariables('{}')
    }
    
    // Clear previous response
    setResponse(null)
    setError(null)
    setResponseTime(null)
  }, [])
  
  // Sync with external selection (from main sidebar)
  useEffect(() => {
    if (externalSelectedId && operations.length > 0) {
      const op = operations.find(o => o.id === externalSelectedId)
      if (op) {
        setQuery(op.query)
        if (op.exampleVariables && Object.keys(op.exampleVariables).length > 0) {
          setVariables(JSON.stringify(op.exampleVariables, null, 2))
        } else {
          setVariables('{}')
        }
        setResponse(null)
        setError(null)
        setResponseTime(null)
      }
    }
  }, [externalSelectedId, operations])

  // Execute GraphQL query
  const executeQuery = useCallback(async () => {
    setIsLoading(true)
    setError(null)
    setResponse(null)
    setResponseTime(null)

    if (abortControllerRef.current) {
      abortControllerRef.current.abort()
    }
    abortControllerRef.current = new AbortController()

    const startTime = performance.now()

    try {
      let parsedVariables = {}
      try {
        parsedVariables = variables.trim() && variables.trim() !== '{}' 
          ? JSON.parse(variables) 
          : {}
      } catch {
        setError('Invalid JSON in variables')
        setIsLoading(false)
        return
      }

      let parsedHeaders: Record<string, string> = {}
      try {
        parsedHeaders = customHeaders.trim() && customHeaders.trim() !== '{}'
          ? JSON.parse(customHeaders) 
          : {}
      } catch {
        setError('Invalid JSON in headers')
        setIsLoading(false)
        return
      }

      const res = await fetch(endpoint, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          Accept: 'application/json',
          ...defaultHeaders,
          ...parsedHeaders,
        },
        body: JSON.stringify({
          query,
          variables: parsedVariables,
        }),
        signal: abortControllerRef.current.signal,
      })

      const endTime = performance.now()
      setResponseTime(Math.round(endTime - startTime))

      const data = await res.json()
      setResponse(data)
    } catch (err) {
      if (err instanceof Error && err.name === 'AbortError') return
      setError(err instanceof Error ? err.message : 'Request failed')
    } finally {
      setIsLoading(false)
    }
  }, [query, variables, customHeaders, endpoint, defaultHeaders])

  const handleCancel = useCallback(() => {
    if (abortControllerRef.current) {
      abortControllerRef.current.abort()
      abortControllerRef.current = null
    }
    setIsLoading(false)
  }, [])

  useEffect(() => {
    const handleKeyDown = (e: KeyboardEvent) => {
      if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
        e.preventDefault()
        if (!isLoading) executeQuery()
      }
    }
    window.addEventListener('keydown', handleKeyDown)
    return () => window.removeEventListener('keydown', handleKeyDown)
  }, [executeQuery, isLoading])

  const copyResponse = useCallback(() => {
    if (response) {
      navigator.clipboard.writeText(JSON.stringify(response, null, 2))
      setCopied(true)
      setTimeout(() => setCopied(false), 2000)
    }
  }, [response])

  const handleReset = useCallback(() => {
    setQuery(defaultQuery)
    setVariables('{}')
    setCustomHeaders(
      Object.keys(defaultHeaders).length > 0 
        ? JSON.stringify(defaultHeaders, null, 2) 
        : '{}'
    )
    setResponse(null)
    setError(null)
    setResponseTime(null)
    setInternalSelectedId(undefined)
    localStorage.removeItem(STORAGE_KEY)
  }, [defaultQuery, defaultHeaders])

  const monacoTheme = theme === 'dark' ? 'vs-dark' : 'vs'
  const hasErrors = response?.errors && response.errors.length > 0
  const statusColor = hasErrors ? 'text-red-400' : response ? 'text-emerald-400' : 'text-zinc-500'

  return (
    <TooltipProvider>
      <div className={cn('flex flex-col h-full bg-background', className)}>
        {/* Toolbar */}
        <div className="flex items-center justify-between gap-2 px-4 py-2 border-b border-border bg-muted/50">
          <div className="flex items-center gap-2">
            <div className="hidden sm:flex items-center gap-2 px-3 py-1.5 rounded-md bg-background border border-border">
              <span className="text-xs font-medium text-emerald-500">POST</span>
              <span className="text-xs text-muted-foreground font-mono truncate max-w-[200px] md:max-w-[400px]">
                {endpoint}
              </span>
            </div>
          </div>

          <div className="flex items-center gap-2">
            <Button
              onClick={isLoading ? handleCancel : executeQuery}
              variant={isLoading ? 'destructive' : 'default'}
              size="sm"
              className="px-4"
            >
              {isLoading ? (
                <>
                  <X className="h-4 w-4 mr-2" weight="bold" />
                  Cancel
                </>
              ) : (
                <>
                  <Play className="h-4 w-4 mr-2" weight="fill" />
                  Run
                </>
              )}
            </Button>

            <Tooltip>
              <TooltipTrigger asChild>
                <Button variant="outline" size="icon" onClick={handleReset}>
                  <ArrowClockwise className="h-4 w-4" weight="bold" />
                </Button>
              </TooltipTrigger>
              <TooltipContent>Reset to defaults</TooltipContent>
            </Tooltip>
          </div>
        </div>

        {/* Main Content */}
        <div className="flex-1 flex overflow-hidden">
          {/* Left Sidebar - Schema Explorer (only shown when not using main sidebar) */}
          {!hideExplorer && operations.length > 0 && (
            <div className="w-64 flex-shrink-0 hidden md:block">
              <SchemaExplorer
                operations={operations}
                onSelectOperation={handleSelectOperation}
                selectedOperationId={selectedOperationId}
              />
            </div>
          )}

          {/* Center & Right - Query/Response */}
          <div className="flex-1 flex flex-col lg:flex-row overflow-hidden">
            {/* Query editor */}
            <div className="flex-1 flex flex-col border-b lg:border-b-0 lg:border-r border-border min-h-[200px] lg:min-h-0">
              <div className="flex-1 flex flex-col min-h-0">
                <div className="flex items-center px-3 py-2 border-b border-border bg-muted/30">
                  <FileText className="h-4 w-4 mr-2 text-muted-foreground" />
                  <span className="text-xs font-medium text-muted-foreground uppercase tracking-wider">
                    Query
                  </span>
                  <span className="ml-auto text-xs text-muted-foreground">
                    Ctrl+Enter to run
                  </span>
                </div>
                <div className="flex-1 min-h-0">
                  <Editor
                    height="100%"
                    language="graphql"
                    value={query}
                    onChange={(value) => {
                      setQuery(value || '')
                      setInternalSelectedId(undefined) // Clear internal selection when manually editing
                    }}
                    theme={monacoTheme}
                    options={{
                      minimap: { enabled: false },
                      scrollBeyondLastLine: false,
                      fontSize: 13,
                      fontFamily: "'JetBrains Mono', 'Fira Code', 'SF Mono', Consolas, monospace",
                      lineNumbers: 'on',
                      renderLineHighlight: 'line',
                      scrollbar: { vertical: 'auto', horizontal: 'auto' },
                      padding: { top: 12, bottom: 12 },
                      wordWrap: 'on',
                      automaticLayout: true,
                      tabSize: 2,
                    }}
                    loading={
                      <div className="flex items-center justify-center h-full text-muted-foreground text-sm">
                        <Spinner className="w-5 h-5 animate-spin mr-2" />
                        Loading editor...
                      </div>
                    }
                  />
                </div>
              </div>
              
              {/* Variables/Headers tabs */}
              <div className="border-t border-border h-[180px] flex flex-col">
                <div className="flex border-b border-border bg-muted/30">
                  <button
                    onClick={() => setActiveTab('variables')}
                    className={cn(
                      'flex items-center gap-1.5 px-4 py-2 text-xs font-medium uppercase tracking-wider transition-colors',
                      activeTab === 'variables'
                        ? 'text-primary border-b-2 border-primary -mb-px bg-background'
                        : 'text-muted-foreground hover:text-foreground'
                    )}
                  >
                    <BracketsCurly className="h-3.5 w-3.5" />
                    Variables
                  </button>
                  <button
                    onClick={() => setActiveTab('headers')}
                    className={cn(
                      'flex items-center gap-1.5 px-4 py-2 text-xs font-medium uppercase tracking-wider transition-colors',
                      activeTab === 'headers'
                        ? 'text-primary border-b-2 border-primary -mb-px bg-background'
                        : 'text-muted-foreground hover:text-foreground'
                    )}
                  >
                    <Key className="h-3.5 w-3.5" />
                    Headers
                  </button>
                </div>
                <div className="flex-1 min-h-0">
                  <Editor
                    height="100%"
                    language="json"
                    value={activeTab === 'variables' ? variables : customHeaders}
                    onChange={(value) => 
                      activeTab === 'variables' 
                        ? setVariables(value || '{}')
                        : setCustomHeaders(value || '{}')
                    }
                    theme={monacoTheme}
                    options={{
                      minimap: { enabled: false },
                      scrollBeyondLastLine: false,
                      fontSize: 13,
                      fontFamily: "'JetBrains Mono', 'Fira Code', 'SF Mono', Consolas, monospace",
                      lineNumbers: 'off',
                      renderLineHighlight: 'none',
                      scrollbar: { vertical: 'auto', horizontal: 'auto' },
                      padding: { top: 8, bottom: 8 },
                      wordWrap: 'on',
                      automaticLayout: true,
                      tabSize: 2,
                      folding: false,
                    }}
                    loading={
                      <div className="flex items-center justify-center h-full text-muted-foreground text-sm">
                        Loading...
                      </div>
                    }
                  />
                </div>
              </div>
            </div>

            {/* Response */}
            <div className="flex-1 flex flex-col min-h-[200px] lg:min-h-0">
              <div className="flex items-center justify-between px-3 py-2 border-b border-border bg-muted/30">
                <div className="flex items-center gap-3">
                  <span className="text-xs font-medium text-muted-foreground uppercase tracking-wider">
                    Response
                  </span>
                  {responseTime !== null && (
                    <span className="text-xs text-muted-foreground">
                      {responseTime}ms
                    </span>
                  )}
                  {response && (
                    <span className={cn('text-xs font-medium', statusColor)}>
                      {hasErrors ? 'Error' : 'Success'}
                    </span>
                  )}
                </div>
                {response && (
                  <Tooltip>
                    <TooltipTrigger asChild>
                      <Button variant="ghost" size="sm" onClick={copyResponse} className="h-7 px-2">
                        {copied ? (
                          <>
                            <Check className="h-3.5 w-3.5 mr-1 text-emerald-500" />
                            <span className="text-xs">Copied</span>
                          </>
                        ) : (
                          <>
                            <Copy className="h-3.5 w-3.5 mr-1" />
                            <span className="text-xs">Copy</span>
                          </>
                        )}
                      </Button>
                    </TooltipTrigger>
                    <TooltipContent>Copy response</TooltipContent>
                  </Tooltip>
                )}
              </div>
              <div className="flex-1 min-h-0 overflow-hidden">
                {isLoading ? (
                  <div className="flex items-center justify-center h-full">
                    <Spinner className="w-6 h-6 animate-spin text-primary" />
                    <span className="ml-2 text-sm text-muted-foreground">Executing query...</span>
                  </div>
                ) : error ? (
                  <div className="p-4">
                    <div className="p-4 bg-destructive/10 border border-destructive/20 rounded-lg">
                      <p className="text-destructive text-sm font-mono">{error}</p>
                    </div>
                  </div>
                ) : response ? (
                  <Editor
                    height="100%"
                    language="json"
                    value={JSON.stringify(response, null, 2)}
                    theme={monacoTheme}
                    options={{
                      readOnly: true,
                      minimap: { enabled: false },
                      scrollBeyondLastLine: false,
                      fontSize: 13,
                      fontFamily: "'JetBrains Mono', 'Fira Code', 'SF Mono', Consolas, monospace",
                      lineNumbers: 'on',
                      renderLineHighlight: 'none',
                      scrollbar: { vertical: 'auto', horizontal: 'auto' },
                      padding: { top: 12, bottom: 12 },
                      wordWrap: 'on',
                      automaticLayout: true,
                      folding: true,
                    }}
                  />
                ) : (
                  <div className="flex flex-col items-center justify-center h-full text-muted-foreground">
                    <Play className="h-12 w-12 mb-3 opacity-20" />
                    <p className="text-sm">Run a query to see results</p>
                    <p className="text-xs mt-1 opacity-60">Press Ctrl+Enter or click Run</p>
                  </div>
                )}
              </div>
            </div>
          </div>
        </div>
      </div>
    </TooltipProvider>
  )
}

export function GraphQLPlaygroundWithExplorer(props: GraphQLPlaygroundProps) {
  return <GraphQLPlayground {...props} />
}

export default GraphQLPlayground
