<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import {
  X,
  Upload,
  FileCode,
  Folder,
  FolderOpen,
  ChevronRight,
  ChevronDown,
  Search,
  Send,
  Loader,
  FileText,
  ExternalLink,
  Trash2,
  RefreshCw,
  FolderPlus,
  Cloud,
  Database,
} from 'lucide-vue-next'
import { folderApi, type FolderTree } from '@/api/folder'
import { documentApi } from '@/api/document'
import { remoteMountApi, type RemoteMount, type RemoteNode } from '@/api/remoteMount'
import { useKnowledgeView } from '@/pages/knowledge/composables/useKnowledgeView'
import './KnowledgeRAG.css'

defineProps<{
  isChatLoading: boolean
}>()

const emit = defineEmits(['close', 'update:chatInput', 'send'])

function onInputChange(event: Event) {
  const target = event.target as HTMLInputElement
  chatInput.value = target.value
  emit('update:chatInput', target.value)
}

const {
  documents,
  folders,
  currentFolderId,
  currentFolder,
  isLoadingDocuments,
  formatFileSize,
  formatDate,
  uploadInput,
  handleDeleteDocument,
  openDocument,
  activeDocumentContent,
  isLoadingDocumentContent,
  enterFolder,
} = useKnowledgeView()

const selectedDoc = ref<any>(null)
const previewOpen = ref(false)
const isUploadingFile = ref(false)
const explorerMode = ref<'local' | 'remote'>('local')

const showNewFolderInput = ref(false)
const newFolderName = ref('')
const newFolderParentId = ref<string | null>(null)
const isCreatingFolder = ref(false)

const showDeleteConfirm = ref(false)
const deletingFolder = ref<FolderTree | null>(null)
const isDeletingFolder = ref(false)

const expandedFolderIds = ref<Set<string>>(new Set())
const remoteExpandedIds = ref<Set<string>>(new Set())
const remoteMounts = ref<RemoteMount[]>([])
const selectedMountId = ref<string | null>(null)
const remoteNodes = ref<RemoteNode[]>([])
const selectedRemotePath = ref<string | null>(null)
const isLoadingRemoteMounts = ref(false)
const isLoadingRemoteTree = ref(false)
const isSyncingRemote = ref<string | null>(null)
const showRemoteMountInput = ref(false)
const remoteMountForm = ref({
  name: '',
  base_url: '',
  username: '',
  password: '',
  root_path: '/',
})

interface RAGMessage {
  id: string
  role: 'user' | 'assistant'
  content: string
  sources?: DocumentSource[]
}

interface DocumentSource {
  id: string
  title: string
  file_type: string
  folder_id?: string | null
  similarity?: number
  chunk_content?: string
}

const chatMessages = ref<RAGMessage[]>([])
const chatInput = ref('')

function ensureExpandedPath(targetId: string | null, nodes: FolderTree[] = folders.value): boolean {
  if (!targetId) return false

  for (const node of nodes) {
    if (node.id === targetId) {
      return true
    }
    if (node.children?.length && ensureExpandedPath(targetId, node.children)) {
      expandedFolderIds.value.add(node.id)
      return true
    }
  }

  return false
}

function getVisibleFolders(nodes: FolderTree[], depth = 0): Array<{ data: FolderTree; depth: number }> {
  const result: Array<{ data: FolderTree; depth: number }> = []

  for (const folder of nodes) {
    result.push({ data: folder, depth })
    if (folder.children?.length && expandedFolderIds.value.has(folder.id)) {
      result.push(...getVisibleFolders(folder.children, depth + 1))
    }
  }

  return result
}

const visibleFolders = computed(() => getVisibleFolders(folders.value))
const ROW_BASE_PADDING = 12
const DEPTH_INDENT = 16
const CONTENT_BASE_PADDING = 36

const newFolderTargetLabel = computed(() => {
  if (!newFolderParentId.value) {
    return 'ROOT'
  }
  return findFolderNameById(newFolderParentId.value) ?? 'TARGET'
})

function hasChildren(folder: FolderTree) {
  return (folder.children?.length ?? 0) > 0
}

function isExpanded(folderId: string) {
  return expandedFolderIds.value.has(folderId)
}

function isFolderOpen(folder: FolderTree) {
  return currentFolderId.value === folder.id || isExpanded(folder.id)
}

function toggleFolder(folder: FolderTree) {
  if (!hasChildren(folder)) {
    return
  }

  if (expandedFolderIds.value.has(folder.id)) {
    expandedFolderIds.value.delete(folder.id)
  } else {
    expandedFolderIds.value.add(folder.id)
  }
  expandedFolderIds.value = new Set(expandedFolderIds.value)
}

function getFolderIcon(folder: FolderTree) {
  return isFolderOpen(folder) ? FolderOpen : Folder
}

function findFolderNameById(targetId: string | null, nodes: FolderTree[] = folders.value): string | null {
  if (!targetId) return null

  for (const node of nodes) {
    if (node.id === targetId) {
      return node.name
    }
    if (node.children?.length) {
      const nested = findFolderNameById(targetId, node.children)
      if (nested) return nested
    }
  }

  return null
}

function getVisibleRemoteNodes(nodes: RemoteNode[], depth = 0): Array<{ data: RemoteNode; depth: number }> {
  const result: Array<{ data: RemoteNode; depth: number }> = []

  for (const node of nodes) {
    result.push({ data: node, depth })
    if (node.is_dir && remoteExpandedIds.value.has(node.path)) {
      result.push(...getVisibleRemoteNodes(node.children, depth + 1))
    }
  }

  return result
}

const visibleRemoteNodes = computed(() => getVisibleRemoteNodes(remoteNodes.value))
const selectedLocalFolderLabel = computed(() => currentFolder.value?.name ?? 'Select in LOCAL first')
const selectedMount = computed(() => remoteMounts.value.find((mount) => mount.id === selectedMountId.value) ?? null)

async function loadRemoteMounts() {
  isLoadingRemoteMounts.value = true
  try {
    const response = await remoteMountApi.list()
    remoteMounts.value = response.data
    if (!selectedMountId.value && response.data.length > 0) {
      selectedMountId.value = response.data[0].id
      await loadRemoteTree(response.data[0].id)
    }
  } catch (error) {
    console.error('Failed to load remote mounts:', error)
  } finally {
    isLoadingRemoteMounts.value = false
  }
}

async function loadRemoteTree(mountId: string) {
  isLoadingRemoteTree.value = true
  try {
    const response = await remoteMountApi.getTree(mountId)
    remoteNodes.value = response.data.nodes
  } catch (error) {
    console.error('Failed to load remote tree:', error)
    remoteNodes.value = []
  } finally {
    isLoadingRemoteTree.value = false
  }
}

async function handleSelectMount(mountId: string) {
  selectedMountId.value = mountId
  selectedRemotePath.value = null
  remoteExpandedIds.value = new Set()
  await loadRemoteTree(mountId)
}

function isRemoteExpanded(path: string) {
  return remoteExpandedIds.value.has(path)
}

function handleRemoteNodeClick(node: RemoteNode) {
  selectedRemotePath.value = node.path
  if (node.is_dir) {
    if (remoteExpandedIds.value.has(node.path)) {
      remoteExpandedIds.value.delete(node.path)
    } else {
      remoteExpandedIds.value.add(node.path)
    }
    remoteExpandedIds.value = new Set(remoteExpandedIds.value)
  }
}

async function createRemoteMount() {
  if (!remoteMountForm.value.name.trim() || !remoteMountForm.value.base_url.trim()) {
    return
  }

  try {
    const response = await remoteMountApi.create({
      name: remoteMountForm.value.name.trim(),
      base_url: remoteMountForm.value.base_url.trim(),
      username: remoteMountForm.value.username.trim() || null,
      password: remoteMountForm.value.password || null,
      root_path: remoteMountForm.value.root_path.trim() || '/',
    })
    showRemoteMountInput.value = false
    remoteMountForm.value = { name: '', base_url: '', username: '', password: '', root_path: '/' }
    await loadRemoteMounts()
    await handleSelectMount(response.data.id)
  } catch (error) {
    console.error('Failed to create remote mount:', error)
  }
}

async function syncRemoteNode(node: RemoteNode) {
  if (!selectedMountId.value || !currentFolderId.value || isSyncingRemote.value) {
    return
  }

  isSyncingRemote.value = node.path
  try {
    await remoteMountApi.sync(selectedMountId.value, {
      remote_path: node.path,
      local_folder_id: currentFolderId.value,
      mode: node.is_dir ? 'folder' : 'file',
    })
    if (currentFolder.value) {
      await enterFolder(currentFolder.value)
    }
  } catch (error) {
    console.error('Failed to sync remote node:', error)
  } finally {
    isSyncingRemote.value = null
  }
}

function openNewFolderDialog(parentId: string | null = null) {
  newFolderParentId.value = parentId
  newFolderName.value = ''
  if (parentId) {
    expandedFolderIds.value.add(parentId)
    expandedFolderIds.value = new Set(expandedFolderIds.value)
  }
  showNewFolderInput.value = true
}

async function createFolderApi(name: string) {
  const normalizedName = name.trim()
  if (!normalizedName) return

  isCreatingFolder.value = true
  try {
    await folderApi.create({
      name: normalizedName,
      parent_id: newFolderParentId.value,
    })
    const response = await folderApi.getTree()
    folders.value = response.data
    ensureExpandedPath(newFolderParentId.value)
    expandedFolderIds.value = new Set(expandedFolderIds.value)
    showNewFolderInput.value = false
    newFolderName.value = ''
  } catch (err) {
    console.error('Failed to create folder:', err)
  } finally {
    isCreatingFolder.value = false
  }
}

function cancelNewFolder() {
  showNewFolderInput.value = false
  newFolderName.value = ''
  newFolderParentId.value = null
}

function openDeleteDialog(folder: FolderTree) {
  deletingFolder.value = folder
  showDeleteConfirm.value = true
}

async function deleteFolderApi() {
  if (!deletingFolder.value) return

  isDeletingFolder.value = true
  try {
    await folderApi.delete(deletingFolder.value.id)
    const response = await folderApi.getTree()
    folders.value = response.data
    if (currentFolderId.value === deletingFolder.value.id) {
      selectedDoc.value = null
    }
    showDeleteConfirm.value = false
    deletingFolder.value = null
  } catch (err) {
    console.error('Failed to delete folder:', err)
  } finally {
    isDeletingFolder.value = false
  }
}

function cancelDelete() {
  showDeleteConfirm.value = false
  deletingFolder.value = null
}

async function handleFolderClick(folder: FolderTree) {
  if (currentFolderId.value === folder.id) {
    if (expandedFolderIds.value.has(folder.id)) {
      expandedFolderIds.value.delete(folder.id)
      expandedFolderIds.value = new Set(expandedFolderIds.value)
      return
    }
    expandedFolderIds.value.add(folder.id)
    expandedFolderIds.value = new Set(expandedFolderIds.value)
    return
  }

  ensureExpandedPath(folder.id)
  expandedFolderIds.value.add(folder.id)
  expandedFolderIds.value = new Set(expandedFolderIds.value)
  await enterFolder(folder)
}

function triggerFolderUpload() {
  if (!currentFolderId.value || isUploadingFile.value) {
    return
  }
  uploadInput.value?.click()
}

function handleFileClick(doc: any) {
  selectedDoc.value = doc
  previewOpen.value = true
  openDocument(doc)
}

async function onUploadChange(event: Event) {
  const target = event.target as HTMLInputElement
  const file = target.files?.[0]
  const selectedFolder = currentFolder.value

  if (!file || !selectedFolder?.id) {
    target.value = ''
    return
  }

  isUploadingFile.value = true
  try {
    await documentApi.upload(file, selectedFolder.id)
    await enterFolder(selectedFolder)
  } catch (error) {
    console.error('Failed to upload document:', error)
  } finally {
    isUploadingFile.value = false
    target.value = ''
  }
}

function formatSimilarity(score?: number) {
  if (!score) return ''
  return `${Math.round(score * 100)}%`
}

function addMessage(msg: RAGMessage) {
  chatMessages.value.push(msg)
}

onMounted(async () => {
  await loadRemoteMounts()
})

defineExpose({ addMessage })
</script>

<template>
  <div class="rag-panel-shell">
    <div class="rag-backdrop" @click="emit('close')"></div>

    <div class="rag-panel">
      <header class="rag-header">
        <div class="header-left">
          <div class="coord-tag">
            <Search :size="10" />
            <span>KNOWLEDGE_RAG</span>
          </div>
        </div>
        <button class="btn-close" aria-label="Close knowledge panel" @click="emit('close')">
          <X :size="16" />
        </button>
      </header>

      <div class="rag-body">
        <aside class="rag-explorer">
          <div class="explorer-toolbar">
            <div class="toolbar-title">
              <component :is="explorerMode === 'local' ? Database : Cloud" :size="12" />
              <span>{{ explorerMode === 'local' ? 'ARCHIVE TREE' : 'REMOTE MOUNTS' }}</span>
            </div>
            <div class="toolbar-actions">
              <template v-if="explorerMode === 'local'">
                <button class="action-btn" @click="openNewFolderDialog(currentFolderId)" title="New folder">
                  <FolderPlus :size="10" />
                  <span>NEW</span>
                </button>
                <button class="action-btn" :disabled="!currentFolderId || isUploadingFile" @click="triggerFolderUpload()">
                  <Upload :size="10" />
                  <span>{{ isUploadingFile ? 'UPLOADING' : 'UPLOAD' }}</span>
                </button>
              </template>
              <template v-else>
                <button class="action-btn" @click="showRemoteMountInput = true" title="New remote mount">
                  <FolderPlus :size="10" />
                  <span>MOUNT</span>
                </button>
              </template>
              <input
                ref="uploadInput"
                type="file"
                accept=".pdf,.md,.txt,.doc,.docx,.csv,.xlsx"
                class="hidden-input"
                @change="onUploadChange"
              />
            </div>
          </div>

          <div class="explorer-mode-switch">
            <button class="mode-btn" :class="{ active: explorerMode === 'local' }" @click="explorerMode = 'local'">LOCAL</button>
            <button class="mode-btn" :class="{ active: explorerMode === 'remote' }" @click="explorerMode = 'remote'">REMOTE</button>
          </div>

          <div v-if="showDeleteConfirm" class="inline-dialog">
            <div class="dialog-content">
              <span class="dialog-label">DELETE FOLDER</span>
              <p class="dialog-text">Delete "{{ deletingFolder?.name }}" and its contents?</p>
              <div class="dialog-actions">
                <button class="action-btn" @click="cancelDelete">CANCEL</button>
                <button class="action-btn danger" @click="deleteFolderApi" :disabled="isDeletingFolder">
                  <Loader v-if="isDeletingFolder" :size="10" class="spin" />
                  <span>DELETE</span>
                </button>
              </div>
            </div>
          </div>

          <div v-if="explorerMode === 'local'" class="folder-tree">
            <div v-if="visibleFolders.length === 0" class="empty-state">
              <span>NO_FOLDERS</span>
            </div>

            <div
              v-for="{ data: folder, depth } in visibleFolders"
              :key="`folder-${folder.id}`"
              class="folder-node"
            >
              <div
                class="folder-row"
                :class="{ active: currentFolderId === folder.id }"
                :style="{ paddingLeft: `${ROW_BASE_PADDING + depth * DEPTH_INDENT}px` }"
                @click="handleFolderClick(folder)"
              >
                <button
                  class="folder-toggle"
                  type="button"
                  @click.stop="handleFolderClick(folder)"
                >
                  <component :is="isFolderOpen(folder) ? ChevronDown : ChevronRight" :size="12" />
                </button>
                <component :is="getFolderIcon(folder)" :size="12" class="folder-icon" />
                <span class="folder-name">{{ folder.name }}</span>
                <div v-if="currentFolderId === folder.id" class="folder-actions">
                  <button class="action-btn small danger" @click.stop="openDeleteDialog(folder)" title="Delete folder">
                    <Trash2 :size="8" />
                  </button>
                </div>
              </div>

              <div
                v-if="currentFolderId === folder.id && !isLoadingDocuments && documents.length > 0"
                class="folder-contents"
                :style="{ paddingLeft: `${CONTENT_BASE_PADDING + depth * DEPTH_INDENT}px` }"
              >
                <div class="folder-file-list">
                  <button
                    v-for="doc in documents"
                    :key="doc.id"
                    class="tree-file-row"
                    :class="{ active: selectedDoc?.id === doc.id }"
                    @click="handleFileClick(doc)"
                  >
                    <span class="tree-file-name">{{ doc.title }}</span>
                    <span class="tree-file-meta">
                      <span>{{ formatDate(doc.created_at) }}</span>
                      <span>{{ formatFileSize(doc.file_size) }}</span>
                      <span class="tree-file-delete" @click.stop="handleDeleteDocument(doc.id)">
                        <Trash2 :size="10" />
                      </span>
                    </span>
                  </button>
                </div>
              </div>
            </div>
          </div>

          <div v-else class="folder-tree remote-tree">
            <div class="remote-mount-strip">
              <select
                class="remote-mount-select"
                :disabled="isLoadingRemoteMounts || remoteMounts.length === 0"
                :value="selectedMountId ?? ''"
                @change="handleSelectMount(($event.target as HTMLSelectElement).value)"
              >
                <option value="" disabled>{{ isLoadingRemoteMounts ? 'Loading mounts...' : 'Select mount' }}</option>
                <option v-for="mount in remoteMounts" :key="mount.id" :value="mount.id">{{ mount.name }}</option>
              </select>
              <button class="action-btn small" :disabled="!selectedMountId || isLoadingRemoteTree" @click="selectedMountId && loadRemoteTree(selectedMountId)">
                <RefreshCw :size="10" />
              </button>
            </div>

            <div class="remote-sync-hint">
              <span>TARGET</span>
              <strong>{{ selectedLocalFolderLabel }}</strong>
            </div>

            <div v-if="isLoadingRemoteTree" class="empty-state">
              <Loader :size="14" class="spin" />
              <span>LOADING_REMOTE_TREE...</span>
            </div>

            <div v-else-if="visibleRemoteNodes.length === 0" class="empty-state">
              <span>{{ selectedMount ? 'NO_REMOTE_ITEMS' : 'NO_MOUNT_SELECTED' }}</span>
            </div>

            <div
              v-for="{ data: node, depth } in visibleRemoteNodes"
              :key="node.path"
              class="folder-node"
            >
              <div
                class="folder-row remote-row"
                :class="{ active: selectedRemotePath === node.path }"
                :style="{ paddingLeft: `${ROW_BASE_PADDING + depth * DEPTH_INDENT}px` }"
                @click="handleRemoteNodeClick(node)"
              >
                <button class="folder-toggle" type="button" @click.stop="handleRemoteNodeClick(node)">
                  <component :is="node.is_dir && isRemoteExpanded(node.path) ? ChevronDown : ChevronRight" :size="12" />
                </button>
                <component :is="node.is_dir ? (isRemoteExpanded(node.path) ? FolderOpen : Folder) : FileCode" :size="12" class="folder-icon" />
                <span class="folder-name">{{ node.name }}</span>
                <button
                  class="action-btn small"
                  :disabled="!currentFolderId || isSyncingRemote === node.path"
                  @click.stop="syncRemoteNode(node)"
                >
                  <Loader v-if="isSyncingRemote === node.path" :size="8" class="spin" />
                  <RefreshCw v-else :size="8" />
                </button>
              </div>
            </div>
          </div>
        </aside>

        <main class="rag-chat">
          <div class="chat-header">
            <div class="chat-title">
              <RefreshCw :size="12" />
              <span>RAG CONVERSATION</span>
            </div>
          </div>

          <div class="chat-messages">
            <div v-if="chatMessages.length === 0" class="chat-welcome">
              <div class="welcome-icon">
                <Search :size="32" />
              </div>
              <p>Use natural language to search the selected knowledge folder.</p>
              <p class="example-hint">Example: "Find the latest project planning notes."</p>
            </div>

            <div
              v-for="msg in chatMessages"
              :key="msg.id"
              class="message-wrapper"
              :class="msg.role"
            >
              <div class="message-bubble">
                <div class="message-content">{{ msg.content }}</div>
                <div v-if="msg.sources?.length" class="sources-list">
                  <div class="sources-label">SOURCES</div>
                  <div
                    v-for="source in msg.sources"
                    :key="source.id"
                    class="source-item"
                    @click="handleFileClick(source)"
                  >
                    <FileCode :size="10" />
                    <span>{{ source.title }}</span>
                    <span v-if="source.similarity" class="similarity">{{ formatSimilarity(source.similarity) }}</span>
                    <ExternalLink :size="8" />
                  </div>
                </div>
              </div>
            </div>

            <div v-if="isChatLoading" class="message-wrapper assistant">
              <div class="message-bubble loading">
                <Loader :size="14" class="spin" />
                <span>SEARCHING...</span>
              </div>
            </div>
          </div>

          <div class="rag-input-area">
            <input
              type="text"
              class="rag-input"
              placeholder="Ask the knowledge base..."
              :value="chatInput"
              @input="onInputChange"
              @keydown.enter="emit('send')"
            />
            <button class="rag-send-btn" @click="emit('send')">
              <Send :size="16" />
            </button>
          </div>
        </main>
      </div>

      <Transition name="fade">
        <div v-if="showNewFolderInput" class="rag-dialog-overlay" @click.self="cancelNewFolder">
          <div class="rag-dialog-card">
            <div class="dialog-content">
              <span class="dialog-label">NEW FOLDER / {{ newFolderTargetLabel }}</span>
              <input
                v-model="newFolderName"
                type="text"
                class="dialog-input"
                placeholder="Folder name..."
                @keydown.enter="createFolderApi(newFolderName)"
                @keydown.esc="cancelNewFolder"
                autofocus
              />
              <div class="dialog-actions">
                <button class="action-btn" @click="cancelNewFolder">CANCEL</button>
                <button class="action-btn primary" @click="createFolderApi(newFolderName)" :disabled="isCreatingFolder">
                  <Loader v-if="isCreatingFolder" :size="10" class="spin" />
                  <span>CREATE</span>
                </button>
              </div>
            </div>
          </div>
        </div>
      </Transition>

      <Transition name="fade">
        <div v-if="showRemoteMountInput" class="rag-dialog-overlay" @click.self="showRemoteMountInput = false">
          <div class="rag-dialog-card">
            <div class="dialog-content">
              <span class="dialog-label">NEW WEBDAV MOUNT</span>
              <input v-model="remoteMountForm.name" type="text" class="dialog-input" placeholder="Mount name" />
              <input v-model="remoteMountForm.base_url" type="text" class="dialog-input" placeholder="https://example.com/dav/" />
              <input v-model="remoteMountForm.username" type="text" class="dialog-input" placeholder="Username" />
              <input v-model="remoteMountForm.password" type="password" class="dialog-input" placeholder="Password" />
              <input v-model="remoteMountForm.root_path" type="text" class="dialog-input" placeholder="/remote/path" />
              <div class="dialog-actions">
                <button class="action-btn" @click="showRemoteMountInput = false">CANCEL</button>
                <button class="action-btn primary" @click="createRemoteMount">CREATE</button>
              </div>
            </div>
          </div>
        </div>
      </Transition>

      <Transition name="fade">
        <div v-if="previewOpen && selectedDoc" class="doc-preview-overlay" @click.self="previewOpen = false">
          <div class="doc-preview-panel">
            <header class="preview-header">
              <div class="preview-title">
                <FileText :size="14" />
                <span>{{ selectedDoc.title }}</span>
              </div>
              <button class="preview-close" @click="previewOpen = false">
                <X :size="16" />
              </button>
            </header>
            <div class="preview-body">
              <div v-if="isLoadingDocumentContent" class="preview-loading">
                <Loader :size="20" class="spin" />
                <span>LOADING_CONTENT...</span>
              </div>
              <pre v-else class="preview-content">{{ activeDocumentContent || 'NO_CONTENT' }}</pre>
            </div>
          </div>
        </div>
      </Transition>
    </div>
  </div>
</template>

<style scoped>
/* Styles defined in KnowledgeRAG.css */
</style>
