import { useState } from 'react'
import { apiFetch } from '@core/lib/api'
import { Button } from '@core/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@core/components/ui/card'
import { Progress } from '@core/components/ui/progress'
import { Alert, AlertDescription } from '@core/components/ui/alert'
import { Badge } from '@core/components/ui/badge'
import { ScrollArea } from '@core/components/ui/scroll-area'
import { Tabs, TabsList, TabsTrigger, TabsContent } from '@core/components/ui/tabs'

interface IngestionResponse {
  success: boolean
  items_created: number
  items_updated: number
  embeddings_generated: number
  errors: string[]
  skipped: string[]
  item_ids?: string[]
}

interface IngestionStats {
  total: number
  processed: number
  created: number
  updated: number
  errors: number
}

type Step = 'idle' | 'chunks_loaded' | 'ingestion_complete' | 'embeddings_complete'

// --- Paste & Ingest sequential ingestion types ---
type IngestStatus = 'queued' | 'ingesting' | 'done' | 'failed'

interface ChunkWithStatus {
  name: string
  file_path: string
  line_start: number
  line_end: number
  chunk_type: string
  code: string
  status: IngestStatus
  error?: string
}

export default function KBIngestionPage() {
  const [currentStep, setCurrentStep] = useState<Step>('idle')
  const [isLoading, setIsLoading] = useState(false)
  const [chunks, setChunks] = useState<any[]>([])
  const [stats, setStats] = useState<IngestionStats | null>(null)
  const [error, setError] = useState<string | null>(null)
  const [logs, setLogs] = useState<string[]>([])
  const [ingestFrom, setIngestFrom] = useState<string>('')
  const [ingestTo, setIngestTo] = useState<string>('')
  const [ingestedItemIds, setIngestedItemIds] = useState<string[]>([])

  const addLog = (message: string) => {
    const timestamp = new Date().toLocaleTimeString()
    setLogs(prev => [...prev, `[${timestamp}] ${message}`])
  }

  const loadChunks = async (): Promise<any[]> => {
    try {
      const response = await apiFetch('/.netlify/functions/custom_cortex-chunks', {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json',
        }
      })

      if (!response.ok) {
        const errorData = await response.json().catch(() => ({ error: 'Unknown error' }))
        throw new Error(`Failed to load chunks: ${errorData.error || response.statusText}`)
      }

      const data = await response.json()
      return data.data?.chunks || []
    } catch (err) {
      throw new Error(`Could not load chunks: ${err instanceof Error ? err.message : 'Unknown error'}`)
    }
  }

  const ingestChunks = async (chunks: any[]): Promise<IngestionResponse> => {
    const response = await apiFetch('/api/custom_kb-ingestion?action=ingest', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        chunks,
        force_update: false
      })
    })

    if (!response.ok) {
      const errorData = await response.json().catch(() => ({ error: 'Unknown error' }))
      throw new Error(`Ingestion failed: ${errorData.error || response.statusText}`)
    }

    const json = await response.json()
    return json.data ?? json
  }

  const generateEmbeddings = async (itemIds: string[]): Promise<IngestionResponse> => {
    const response = await apiFetch('/api/custom_kb-embeddings?action=generate', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({
        item_ids: itemIds,
        vector_types: ['semantic', 'structure'],
        force_regenerate: false
      })
    })

    if (!response.ok) {
      const errorData = await response.json().catch(() => ({ error: 'Unknown error' }))
      throw new Error(`Embedding generation failed: ${errorData.error || response.statusText}`)
    }

    const json = await response.json()
    return json.data ?? json
  }

  const handleLoadChunks = async () => {
    setIsLoading(true)
    setError(null)
    setLogs([])

    try {
      addLog('Loading chunks from file...')
      const loadedChunks = await loadChunks()
      addLog(`Loaded ${loadedChunks.length} chunks`)
      setChunks(loadedChunks)
      setCurrentStep('chunks_loaded')
    } catch (err) {
      const errorMessage = err instanceof Error ? err.message : 'Unknown error'
      setError(errorMessage)
      addLog(`Error: ${errorMessage}`)
    } finally {
      setIsLoading(false)
    }
  }

  const handleIngestChunks = async () => {
    setIsLoading(true)
    setError(null)
    setLogs([])

    try {
      // Parse range inputs
      const from = parseInt(ingestFrom) || 1
      const to = parseInt(ingestTo) || chunks.length
      
      // Validate range
      if (from < 1 || to < 1 || from > to || from > chunks.length || to > chunks.length) {
        throw new Error(`Invalid range: Please enter values between 1 and ${chunks.length}, with from ≤ to`)
      }
      
      // Get the subset of chunks (convert to 0-based index)
      const selectedChunks = chunks.slice(from - 1, to)
      const recordCount = selectedChunks.length
      
      addLog(`Starting ingestion of records ${from} through ${to} (${recordCount} records)...`)
      const response = await ingestChunks(selectedChunks)
      
      const newStats: IngestionStats = {
        total: recordCount,
        processed: recordCount,
        created: response.items_created || 0,
        updated: response.items_updated || 0,
        errors: response.errors?.length || 0
      }
      
      setStats(newStats)
      setIngestedItemIds(response.item_ids || [])
      addLog(`Ingestion complete: ${response.items_created || 0} created, ${response.items_updated || 0} updated`)
      addLog(`Captured ${(response.item_ids || []).length} item IDs for embedding generation`)
      
      if (response.errors && response.errors.length > 0) {
        addLog(`Errors: ${response.errors.join(', ')}`)
      }
      
      setCurrentStep('ingestion_complete')
    } catch (err) {
      const errorMessage = err instanceof Error ? err.message : 'Unknown error'
      setError(errorMessage)
      addLog(`Error: ${errorMessage}`)
    } finally {
      setIsLoading(false)
    }
  }

  const handleGenerateEmbeddings = async () => {
    setIsLoading(true)
    setError(null)
    setLogs([])

    try {
      addLog('Generating embeddings for ingested items...')
      const itemIds = ingestedItemIds
      if (itemIds.length === 0) {
        throw new Error('No item IDs available — run ingestion first')
      }
      addLog(`Generating embeddings for ${itemIds.length} items...`)
      const response = await generateEmbeddings(itemIds)
      
      addLog(`Embeddings complete: ${(response as any).embeddings_created || response.embeddings_generated || 0} generated`)
      setCurrentStep('embeddings_complete')
    } catch (err) {
      const errorMessage = err instanceof Error ? err.message : 'Unknown error'
      setError(errorMessage)
      addLog(`Error: ${errorMessage}`)
    } finally {
      setIsLoading(false)
    }
  }

  return (
    <div className="max-w-4xl mx-auto p-6 space-y-6">
      <Tabs defaultValue="spine-code">
        <TabsList className="mb-6">
          <TabsTrigger value="spine-code">Spine Code Chunks</TabsTrigger>
          <TabsTrigger value="paste">Paste &amp; Ingest</TabsTrigger>
          <TabsTrigger value="app-code">App Code &amp; Schema</TabsTrigger>
        </TabsList>

        <TabsContent value="spine-code">
      <Card>
        <CardHeader>
          <CardTitle>KB Code Chunk Ingestion</CardTitle>
          <CardDescription>
            Step-by-step ingestion of parsed code chunks from v2-core functions into the Knowledge Base system.
          </CardDescription>
        </CardHeader>
        <CardContent className="space-y-6">
          {/* Step 1: Load Chunks */}
          <Card>
            <CardHeader>
              <CardTitle className="text-lg">Step 1: Load Chunk Data</CardTitle>
              <CardDescription>
                Load parsed code chunks from the chunks.json file
              </CardDescription>
            </CardHeader>
            <CardContent className="space-y-4">
              <Button 
                onClick={handleLoadChunks}
                disabled={isLoading}
                size="lg"
              >
                {isLoading ? 'Loading...' : currentStep === 'idle' ? 'Load Chunk Data' : 'Reload Chunk Data'}
              </Button>
              
              {currentStep !== 'idle' && (
                <div className="space-y-2">
                  <Badge variant="outline">
                    Chunks Loaded: {chunks.length}
                  </Badge>
                </div>
              )}
            </CardContent>
          </Card>

          {/* Step 2: Display Chunks */}
          {currentStep !== 'idle' && chunks.length > 0 && (
            <Card>
              <CardHeader>
                <CardTitle className="text-lg">Chunk Data Preview</CardTitle>
                <CardDescription>
                  {chunks.length} chunks loaded from file
                </CardDescription>
              </CardHeader>
              <CardContent>
                <ScrollArea className="h-64 w-full border rounded-md p-4">
                  <div className="space-y-2">
                    {chunks.slice(0, 10).map((chunk, index) => (
                      <div key={index} className="text-sm border-b pb-2">
                        <div className="font-medium">{chunk.identifier}</div>
                        <div className="text-gray-600">{chunk.macro}: {chunk.micro}</div>
                        <div className="text-xs text-gray-500">
                          {chunk.chunk_id} • {chunk.version}
                        </div>
                      </div>
                    ))}
                    {chunks.length > 10 && (
                      <div className="text-sm text-gray-500 italic">
                        ... and {chunks.length - 10} more chunks
                      </div>
                    )}
                  </div>
                </ScrollArea>
              </CardContent>
            </Card>
          )}

          {/* Step 3: Ingest Chunks */}
          {currentStep === 'chunks_loaded' && (
            <Card>
              <CardHeader>
                <CardTitle className="text-lg">Step 2: Ingest Chunks</CardTitle>
                <CardDescription>
                  Create KB articles from the loaded chunks
                </CardDescription>
              </CardHeader>
              <CardContent className="space-y-4">
                <div className="flex items-center gap-4">
                  <div className="flex items-center gap-2">
                    <label htmlFor="ingest-from" className="text-sm font-medium">Ingest records</label>
                    <input
                      id="ingest-from"
                      type="number"
                      min="1"
                      max={chunks.length}
                      value={ingestFrom}
                      onChange={(e) => setIngestFrom(e.target.value)}
                      placeholder="1"
                      className="w-20 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
                    />
                    <span className="text-sm font-medium">through</span>
                    <input
                      id="ingest-to"
                      type="number"
                      min="1"
                      max={chunks.length}
                      value={ingestTo}
                      onChange={(e) => setIngestTo(e.target.value)}
                      placeholder={chunks.length.toString()}
                      className="w-20 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
                    />
                  </div>
                  
                  <div className="text-sm text-gray-600">
                    ({(() => {
                      const from = parseInt(ingestFrom) || 1
                      const to = parseInt(ingestTo) || chunks.length
                      const count = from > 0 && to > 0 && from <= to && to <= chunks.length 
                        ? to - from + 1 
                        : 0
                      return count > 0 ? `${count} records` : 'Invalid range'
                    })()})
                  </div>
                </div>
                
                <Button 
                  onClick={handleIngestChunks}
                  disabled={isLoading || !ingestFrom || !ingestTo}
                  size="lg"
                  variant="default"
                >
                  {isLoading ? 'Ingesting...' : `Ingest ${(() => {
                    const from = parseInt(ingestFrom) || 1
                    const to = parseInt(ingestTo) || chunks.length
                    const count = from > 0 && to > 0 && from <= to && to <= chunks.length 
                      ? to - from + 1 
                      : 0
                    return count
                  })()} Records`}
                </Button>
                
                {stats && (
                  <div className="flex gap-2">
                    <Badge variant="outline">
                      Total: {stats.total}
                    </Badge>
                    <Badge variant="outline">
                      Created: {stats.created}
                    </Badge>
                    <Badge variant="outline">
                      Updated: {stats.updated}
                    </Badge>
                    {stats.errors > 0 && (
                      <Badge variant="destructive">
                        Errors: {stats.errors}
                      </Badge>
                    )}
                  </div>
                )}
              </CardContent>
            </Card>
          )}

          {/* Step 4: Generate Embeddings */}
          {currentStep === 'ingestion_complete' && stats && stats.created > 0 && (
            <Card>
              <CardHeader>
                <CardTitle className="text-lg">Step 3: Generate Embeddings</CardTitle>
                <CardDescription>
                  Generate vector embeddings for the {stats.created} created KB articles
                </CardDescription>
              </CardHeader>
              <CardContent className="space-y-4">
                <Button 
                  onClick={handleGenerateEmbeddings}
                  disabled={isLoading}
                  size="lg"
                  variant="default"
                >
                  {isLoading ? 'Generating...' : 'Generate Embeddings'}
                </Button>
              </CardContent>
            </Card>
          )}

          {/* Error Display */}
          {error && (
            <Alert variant="destructive">
              <AlertDescription>{error}</AlertDescription>
            </Alert>
          )}

          {/* Activity Log */}
          {logs.length > 0 && (
            <Card>
              <CardHeader>
                <CardTitle className="text-lg">Activity Log</CardTitle>
              </CardHeader>
              <CardContent>
                <ScrollArea className="h-64 w-full">
                  <div className="bg-gray-50 dark:bg-gray-900 p-4 rounded-lg">
                    <pre className="text-xs font-mono whitespace-pre-wrap">
                      {logs.join('\n')}
                    </pre>
                  </div>
                </ScrollArea>
              </CardContent>
            </Card>
          )}
        </CardContent>
      </Card>
        </TabsContent>

        <TabsContent value="paste">
          <PasteIngestTab />
        </TabsContent>

        <TabsContent value="app-code">
          <AppCodeSchemaTab />
        </TabsContent>
      </Tabs>
    </div>
  )
}

// --- Paste & Ingest Tab ---

type ChunkType = 'function' | 'class' | 'interface' | 'config' | 'component' | 'hook' | 'route' | 'schema'

interface FileEntry {
  id: string
  filePath: string
  content: string
}

interface ParsedRawChunk {
  name: string
  file_path: string
  line_start: number
  line_end: number
  chunk_type: ChunkType
  code: string
}

function detectExtension(filePath: string): string {
  return filePath.split('.').pop()?.toLowerCase() || ''
}

function parseFileIntoChunks(filePath: string, content: string): ParsedRawChunk[] {
  const ext = detectExtension(filePath)
  const lines = content.split('\n')

  // JSON — treat as single chunk
  if (ext === 'json') {
    return [{
      name: filePath.split('/').pop() || filePath,
      file_path: filePath,
      line_start: 1,
      line_end: lines.length,
      chunk_type: 'config',
      code: content,
    }]
  }

  // Markdown — split on ## headings
  if (ext === 'md' || ext === 'mdx') {
    const chunks: ParsedRawChunk[] = []
    let currentLines: string[] = []
    let currentName = filePath.split('/').pop() || filePath
    let startLine = 1

    lines.forEach((line, i) => {
      if (line.startsWith('## ') && currentLines.length > 0) {
        chunks.push({
          name: currentName,
          file_path: filePath,
          line_start: startLine,
          line_end: i,
          chunk_type: 'config',
          code: currentLines.join('\n'),
        })
        currentName = line.replace(/^#+\s*/, '').trim()
        startLine = i + 1
        currentLines = [line]
      } else {
        currentLines.push(line)
      }
    })
    if (currentLines.length > 0) {
      chunks.push({
        name: currentName,
        file_path: filePath,
        line_start: startLine,
        line_end: lines.length,
        chunk_type: 'config',
        code: currentLines.join('\n'),
      })
    }
    return chunks
  }

  // TS / TSX / JS / JSX — split on CHUNK_START markers if present, otherwise split on export/function/class declarations
  if (['ts', 'tsx', 'js', 'jsx'].includes(ext)) {
    // Try chunk markers first
    const markerChunks = parseByChunkMarkers(filePath, lines)
    if (markerChunks.length > 0) return markerChunks
    // Fallback: split on top-level export/function/class declarations
    return parseByDeclarations(filePath, lines)
  }

  // Fallback — single chunk for anything else
  return [{
    name: filePath.split('/').pop() || filePath,
    file_path: filePath,
    line_start: 1,
    line_end: lines.length,
    chunk_type: 'config',
    code: content,
  }]
}

function parseByChunkMarkers(filePath: string, lines: string[]): ParsedRawChunk[] {
  const chunks: ParsedRawChunk[] = []
  let inChunk = false
  let chunkName = ''
  let startLine = 0
  let chunkLines: string[] = []

  lines.forEach((line, i) => {
    const startMatch = line.match(/CHUNK_START:\s*([A-Z0-9_]+)/)
    const endMatch = line.match(/CHUNK_END:/)

    if (startMatch) {
      inChunk = true
      chunkName = startMatch[1].toLowerCase().replace(/_/g, ' ')
      startLine = i + 1
      chunkLines = []
    } else if (endMatch && inChunk) {
      chunks.push({
        name: chunkName,
        file_path: filePath,
        line_start: startLine,
        line_end: i,
        chunk_type: guessChunkType(chunkLines.join('\n')),
        code: chunkLines.join('\n'),
      })
      inChunk = false
      chunkLines = []
    } else if (inChunk) {
      chunkLines.push(line)
    }
  })

  return chunks
}

function parseByDeclarations(filePath: string, lines: string[]): ParsedRawChunk[] {
  const chunks: ParsedRawChunk[] = []
  const declarationRe = /^export\s+(async\s+)?(function|class|const|interface|type)\s+(\w+)/

  let currentName = ''
  let startLine = 0
  let currentLines: string[] = []

  const flush = (endLine: number) => {
    if (currentName && currentLines.length > 0) {
      chunks.push({
        name: currentName,
        file_path: filePath,
        line_start: startLine,
        line_end: endLine,
        chunk_type: guessChunkType(currentLines.join('\n')),
        code: currentLines.join('\n'),
      })
    }
  }

  lines.forEach((line, i) => {
    const m = line.match(declarationRe)
    if (m) {
      flush(i)
      currentName = m[3]
      startLine = i + 1
      currentLines = [line]
    } else if (currentName) {
      currentLines.push(line)
    }
  })
  flush(lines.length)

  // If nothing matched, return the whole file as one chunk
  if (chunks.length === 0) {
    return [{
      name: filePath.split('/').pop() || filePath,
      file_path: filePath,
      line_start: 1,
      line_end: lines.length,
      chunk_type: 'config',
      code: lines.join('\n'),
    }]
  }

  return chunks
}

function guessChunkType(code: string): ChunkType {
  if (/^export\s+(default\s+)?function\s+[A-Z]/.test(code) || /\breturn\s+\(/.test(code)) return 'component'
  if (/^export\s+(async\s+)?function\s+use[A-Z]/.test(code)) return 'hook'
  if (/\bclass\b/.test(code)) return 'class'
  if (/^export\s+interface\b/.test(code)) return 'interface'
  if (/Route|router|app\.(get|post|patch|delete)/.test(code)) return 'route'
  return 'function'
}

function PasteIngestTab() {
  const [files, setFiles] = useState<FileEntry[]>([{ id: crypto.randomUUID(), filePath: '', content: '' }])
  const [appName, setAppName] = useState('spine-framework')
  const [preview, setPreview] = useState<ParsedRawChunk[] | null>(null)
  const [isLoading, setIsLoading] = useState(false)
  const [result, setResult] = useState<{ success: boolean; message: string } | null>(null)
  
  // Sequential ingestion state
  const [chunksWithStatus, setChunksWithStatus] = useState<ChunkWithStatus[]>([])
  const [isIngestingAll, setIsIngestingAll] = useState(false)
  const [currentChunkIndex, setCurrentChunkIndex] = useState<number | null>(null)
  const [cancelRequested, setCancelRequested] = useState(false)

  const addFile = () => setFiles(f => [...f, { id: crypto.randomUUID(), filePath: '', content: '' }])
  const removeFile = (id: string) => setFiles(f => f.filter(e => e.id !== id))
  const updateFile = (id: string, field: keyof Omit<FileEntry, 'id'>, value: string) =>
    setFiles(f => f.map(e => e.id === id ? { ...e, [field]: value } : e))

  const handlePreview = () => {
    const allChunks: ParsedRawChunk[] = []
    for (const file of files) {
      if (!file.filePath.trim() || !file.content.trim()) continue
      allChunks.push(...parseFileIntoChunks(file.filePath.trim(), file.content.trim()))
    }
    setPreview(allChunks)
    // Initialize chunks with status for sequential ingestion
    const chunksWithStatus: ChunkWithStatus[] = allChunks.map(chunk => ({
      ...chunk,
      status: 'queued' as IngestStatus
    }))
    setChunksWithStatus(chunksWithStatus)
    setResult(null)
  }

  const ingestSingleChunk = async (chunk: ChunkWithStatus): Promise<{ success: boolean; error?: string }> => {
    try {
      const response = await apiFetch('/api/custom_code-ingestion?action=ingest_raw', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ chunks: [chunk], app_name: appName }),
        // Add timeout to avoid hanging
        signal: AbortSignal.timeout(30000) // 30 seconds
      })

      if (!response.ok) {
        const errData = await response.json().catch(() => ({ error: response.statusText }))
        return { success: false, error: errData.error || response.statusText }
      }

      return { success: true }
    } catch (err) {
      if (err instanceof Error) {
        if (err.name === 'AbortError') {
          return { success: false, error: 'Request timed out' }
        }
        return { success: false, error: err.message }
      }
      return { success: false, error: 'Unknown error' }
    }
  }

  const retryChunk = async (index: number) => {
    const chunk = chunksWithStatus[index]
    if (!chunk) return

    setChunksWithStatus(prev => prev.map((c, idx) => 
      idx === index ? { ...c, status: 'ingesting', error: undefined } : c
    ))

    const result = await ingestSingleChunk(chunk)
    setChunksWithStatus(prev => prev.map((c, idx) => 
      idx === index ? { ...c, status: result.success ? 'done' : 'failed', error: result.error } : c
    ))
  }

  const handleIngest = async () => {
    if (!chunksWithStatus || chunksWithStatus.length === 0) return
    setIsLoading(true)
    setIsIngestingAll(true)
    setCancelRequested(false)
    setResult(null)

    let created = 0
    let updated = 0
    let skipped = 0
    let errors: string[] = []

    for (let i = 0; i < chunksWithStatus.length; i++) {
      if (cancelRequested) break

      const chunk = chunksWithStatus[i]
      if (chunk.status === 'done') continue

      setCurrentChunkIndex(i)
      setChunksWithStatus(prev => prev.map((c, idx) => 
        idx === i ? { ...c, status: 'ingesting', error: undefined } : c
      ))

      const result = await ingestSingleChunk(chunk)

      if (result.success) {
        setChunksWithStatus(prev => prev.map((c, idx) => 
          idx === i ? { ...c, status: 'done' } : c
        ))
        created += 1 // Simplified: assume all are creates for now
      } else {
        setChunksWithStatus(prev => prev.map((c, idx) => 
          idx === i ? { ...c, status: 'failed', error: result.error } : c
        ))
        errors.push(`${chunk.name}: ${result.error}`)
      }

      // Small delay between chunks to avoid rate limiting
      await new Promise(r => setTimeout(r, 500))
    }

    setCurrentChunkIndex(null)
    setIsIngestingAll(false)

    const totalProcessed = chunksWithStatus.filter(c => c.status === 'done').length
    setResult({
      success: errors.length === 0,
      message: `Done. ${created} created, ${updated} updated, ${skipped} skipped. ${totalProcessed}/${chunksWithStatus.length} chunks processed.${errors.length ? ' Errors: ' + errors.join('; ') : ''}`,
    })

    setIsLoading(false)
  }

  const handleCancel = () => {
    setCancelRequested(true)
    setIsIngestingAll(false)
    setCurrentChunkIndex(null)
    setIsLoading(false)
  }

  return (
    <Card>
      <CardHeader>
        <CardTitle>Paste &amp; Ingest</CardTitle>
        <CardDescription>
          Paste source files directly. Supports .ts, .tsx, .js, .jsx, .json, .md — split automatically into chunks and ingested as KB code articles.
        </CardDescription>
      </CardHeader>
      <CardContent className="space-y-6">

        {/* App name */}
        <div className="flex items-center gap-3">
          <label className="text-sm font-medium shrink-0">App / source name</label>
          <input
            value={appName}
            onChange={e => setAppName(e.target.value)}
            placeholder="e.g. spine-framework"
            className="flex-1 px-3 py-1.5 text-sm border border-border rounded-md bg-background focus:outline-none focus:ring-2 focus:ring-primary"
          />
        </div>

        {/* File entries */}
        <div className="space-y-4">
          {files.map((file, idx) => (
            <div key={file.id} className="border border-border rounded-lg p-4 space-y-3">
              <div className="flex items-center gap-2">
                <span className="text-xs font-medium text-muted-foreground shrink-0">File {idx + 1}</span>
                <input
                  value={file.filePath}
                  onChange={e => updateFile(file.id, 'filePath', e.target.value)}
                  placeholder=".framework/functions/admin-data.ts"
                  className="flex-1 px-3 py-1.5 text-sm border border-border rounded-md bg-background font-mono focus:outline-none focus:ring-2 focus:ring-primary"
                />
                {files.length > 1 && (
                  <button
                    onClick={() => removeFile(file.id)}
                    className="text-xs text-destructive hover:underline shrink-0"
                  >
                    Remove
                  </button>
                )}
              </div>
              <textarea
                value={file.content}
                onChange={e => updateFile(file.id, 'content', e.target.value)}
                placeholder="Paste file content here…"
                rows={12}
                className="w-full px-3 py-2 text-xs font-mono border border-border rounded-md bg-muted focus:outline-none focus:ring-2 focus:ring-primary resize-y"
              />
            </div>
          ))}
        </div>

        <div className="flex gap-2">
          <button
            onClick={addFile}
            className="text-sm text-primary hover:underline"
          >
            + Add another file
          </button>
        </div>

        {/* Actions */}
        <div className="flex gap-3">
          <Button variant="outline" onClick={handlePreview} disabled={files.every(f => !f.content.trim())}>
            Preview Chunks
          </Button>
          <Button onClick={handleIngest} disabled={isLoading || !chunksWithStatus || chunksWithStatus.length === 0 || chunksWithStatus.every(c => c.status === 'done')}>
            {isIngestingAll ? 'Ingesting…' : `Ingest${chunksWithStatus ? ` ${chunksWithStatus.filter(c => c.status === 'queued').length} Selected` : ''}`}
          </Button>
        </div>

        {/* Preview with status */}
        {preview && (
          <div className="space-y-2">
            <div className="flex items-center justify-between">
              <p className="text-sm font-medium">{preview.length} chunks parsed</p>
              {isIngestingAll && (
                <div className="flex items-center gap-2 text-sm text-muted-foreground">
                  <span>Ingesting chunk {currentChunkIndex !== null ? currentChunkIndex + 1 : 0}/{chunksWithStatus.length}</span>
                  <Button variant="outline" size="sm" onClick={handleCancel}>Cancel</Button>
                </div>
              )}
            </div>
            <ScrollArea className="h-56 w-full border rounded-md p-3 bg-muted/50">
              <div className="space-y-2">
                {chunksWithStatus.map((chunk, i) => (
                  <div key={i} className="text-xs border-b border-border/50 pb-2 last:border-0">
                    <div className="flex items-center gap-2">
                      <input
                        type="checkbox"
                        checked={chunk.status !== 'queued'}
                        disabled={isIngestingAll}
                        className="w-3 h-3"
                        onChange={() => {
                          if (!isIngestingAll) {
                            setChunksWithStatus(prev => prev.map((c, idx) => 
                              idx === i ? { ...c, status: c.status === 'queued' ? 'done' : 'queued' } : c
                            ))
                          }
                        }}
                      />
                      <Badge variant="outline" className="text-[10px]">{chunk.chunk_type}</Badge>
                      <span className="font-mono font-medium">{chunk.name}</span>
                      {chunk.status === 'ingesting' && <span className="text-primary animate-pulse">Ingesting...</span>}
                      {chunk.status === 'done' && <span className="text-green-600">✓</span>}
                      {chunk.status === 'failed' && (
                        <div className="flex items-center gap-1">
                          <span className="text-red-600">✗</span>
                          <Button variant="outline" size="sm" onClick={() => retryChunk(i)}>Retry</Button>
                        </div>
                      )}
                    </div>
                    <div className="text-muted-foreground mt-0.5">{chunk.file_path} · lines {chunk.line_start}–{chunk.line_end} · {chunk.code.split('\n').length} lines</div>
                    {chunk.error && (
                      <div className="text-red-600 text-xs mt-1">Error: {chunk.error}</div>
                    )}
                  </div>
                ))}
              </div>
            </ScrollArea>
          </div>
        )}

        {/* Result */}
        {result && (
          <Alert variant={result.success ? 'default' : 'destructive'}>
            <AlertDescription>{result.message}</AlertDescription>
          </Alert>
        )}

      </CardContent>
    </Card>
  )
}

// --- App Code & Schema Tab ---
type UploadFormat = 'raw_chunks' | 'db_schema'

function AppCodeSchemaTab() {
  const [file, setFile] = useState<File | null>(null)
  const [format, setFormat] = useState<UploadFormat>('raw_chunks')
  const [isLoading, setIsLoading] = useState(false)
  const [result, setResult] = useState<{ success: boolean; message: string } | null>(null)

  const handleIngest = async () => {
    if (!file) return
    setIsLoading(true)
    setResult(null)

    try {
      const text = await file.text()
      const parsedData = JSON.parse(text)

      let response: Response
      if (format === 'raw_chunks') {
        response = await apiFetch('/api/custom_code-ingestion?action=ingest_raw', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ chunks: parsedData }),
        })
      } else {
        response = await apiFetch('/api/custom_schema-ingestion?action=ingest', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ tables: parsedData }),
        })
      }

      if (!response.ok) {
        const errData = await response.json().catch(() => ({ error: response.statusText }))
        throw new Error(errData.error || response.statusText)
      }

      const json = await response.json()
      const data = json.data ?? json
      setResult({
        success: true,
        message: `Ingestion complete. ${data.items_created != null ? `${data.items_created} created, ${data.items_updated ?? 0} updated.` : 'Done.'}`,
      })
    } catch (err) {
      setResult({
        success: false,
        message: err instanceof Error ? err.message : 'Unknown error',
      })
    } finally {
      setIsLoading(false)
    }
  }

  return (
    <Card>
      <CardHeader>
        <CardTitle>App Code &amp; Schema Ingestion</CardTitle>
        <CardDescription>
          Upload a JSON file containing raw code chunks or database schema tables to ingest into the Knowledge Base.
        </CardDescription>
      </CardHeader>
      <CardContent className="space-y-6">
        {/* Format selector */}
        <div className="space-y-2">
          <p className="text-sm font-medium">Select format</p>
          <div className="flex gap-6">
            <label className="flex items-center gap-2 cursor-pointer">
              <input
                type="radio"
                name="upload-format"
                value="raw_chunks"
                checked={format === 'raw_chunks'}
                onChange={() => setFormat('raw_chunks')}
                className="accent-primary"
              />
              <span className="text-sm">Raw Code Chunks</span>
            </label>
            <label className="flex items-center gap-2 cursor-pointer">
              <input
                type="radio"
                name="upload-format"
                value="db_schema"
                checked={format === 'db_schema'}
                onChange={() => setFormat('db_schema')}
                className="accent-primary"
              />
              <span className="text-sm">DB Schema (Tables)</span>
            </label>
          </div>
        </div>

        {/* File input */}
        <div className="space-y-2">
          <p className="text-sm font-medium">Upload JSON file</p>
          <input
            type="file"
            accept=".json,application/json"
            onChange={e => setFile(e.target.files?.[0] ?? null)}
            className="block text-sm text-muted-foreground file:mr-4 file:py-1.5 file:px-3 file:rounded file:border file:border-border file:text-xs file:font-medium file:bg-muted file:text-foreground hover:file:bg-accent cursor-pointer"
          />
          {file && (
            <p className="text-xs text-muted-foreground">{file.name} ({(file.size / 1024).toFixed(1)} KB)</p>
          )}
        </div>

        {/* Ingest button */}
        <Button onClick={handleIngest} disabled={isLoading || !file} size="lg">
          {isLoading ? 'Ingesting…' : 'Ingest'}
        </Button>

        {/* Result */}
        {result && (
          <Alert variant={result.success ? 'default' : 'destructive'}>
            <AlertDescription>{result.message}</AlertDescription>
          </Alert>
        )}
      </CardContent>
    </Card>
  )
}
