<template>
  <div class="flex-1 flex flex-col sm:flex-row overflow-hidden">
    <input
      ref="fileInput"
      type="file"
      multiple
      class="hidden"
      :accept="filesExtensions"
      @change="handleFileSelection"
    />
    <div class="w-full flex flex-col sm:h-full">
      <!-- Upload Section -->
      <div class="rounded-lg md:border md:p-5 w-full">
        <div class="flex items-center gap-2 pt-3 sm:pt-0 pb-2">
          <i class="dku-icon-file-text-16 text-primary my-auto"></i>
          <h3 class="text-xl font-semibold">Documents Upload</h3>
        </div>

        <div class="shrink-0">
          <div class="flex items-center gap-1">
            <Label class="font-medium">Add documents</Label>
          </div>
          <p class="text-muted-foreground">Supports PDF, DOCX, PPTX, JPG/PNG, TXT, MD, HTML</p>

          <!-- Desktop Upload Area -->
          <div v-if="isDesktop" class="w-full mt-1">
            <div
              @dragenter.prevent="handleDragEnter"
              @dragover.prevent
              @dragleave.prevent="handleDragLeave"
              @drop.prevent="handleDrop"
              class="border-2 border-dashed rounded-lg p-4 text-center transition-colors cursor-pointer"
              :class="{
                'border-indigo-500 bg-indigo-50': isDragging,
                'border-gray-300 hover:border-gray-400': !isDragging,
                'opacity-50 cursor-not-allowed': uploadDisabled || isBusy,
              }"
              @click="!uploadDisabled && !isBusy && fileInput?.click()"
            >
              <template v-if="isBusy">
                <div class="flex items-center space-x-2 text-gray-500">
                  <div class="flex items-center space-x-2 mx-auto">
                    <Loader2 class="animate-spin w-5 h-5" />
                    <span>Uploading…</span>
                  </div>
                </div>
              </template>
              <template v-else>
                <div class="flex w-full items-center justify-center">
                  <Button variant="outline">
                    <i class="dku-icon-file-16"></i>
                    Select document(s)
                  </Button>
                  <span class="text-sm ml-2"> or drag and drop </span>
                </div>
              </template>
            </div>
          </div>

          <!-- Mobile Upload Button -->
          <div v-if="!isDesktop" class="w-full pb-5 sm:pb-1 mt-1">
            <Button
              variant="outline"
              :disabled="uploadDisabled || isBusy"
              @click="!uploadDisabled && !isBusy && fileInput?.click()"
            >
              <i class="dku-icon-file-upload-16"></i>
              Select document(s)
            </Button>
          </div>
        </div>
        <div class="flex justify-end mt-2" v-if="noActiveDocs.length">
          <Button @click="launchIndexing" :disabled="isBusy">
            <i class="dku-icon-file-upload-16 mr-1"></i>
            <span>Process Document(s)</span>
          </Button>
        </div>
      </div>

      <!-- Search and Actions -->
      <div
        v-if="normalizedDocs.length > 0 || isBusy"
        class="rounded-lg md:border md:p-5 w-full sm:mt-5"
      >
        <div class="flex items-center gap-2 pb-2">
          <i class="dku-icon-dataset-files-in-folder-16 text-primary my-auto"></i>
          <h3 class="text-xl font-semibold">Document Library</h3>
        </div>
        <div class="flex flex-col sm:flex-row justify-between w-full pb-5">
          <div class="relative">
            <i class="dku-icon-search-16 absolute left-3 top-1/2 transform -translate-y-1/2" />
            <Input v-model="searchQuery" placeholder="Search" class="pl-10" />
          </div>

          <div class="flex items-center justify-between sm mt-3">
            <div class="flex items-center space-x-2">
              <span class="mr-2 text-sm">Sort by</span>
              <Select v-model="sortBy" class="w-32">
                <SelectTrigger>
                  <SelectValue />
                </SelectTrigger>
                <SelectContent>
                  <SelectItem value="name">Name</SelectItem>
                  <SelectItem value="size">Size</SelectItem>
                </SelectContent>
              </Select>
            </div>
          </div>
        </div>

        <div class="flex justify-between items-center pb-3">
          <span class="text-sm text-foreground">
            {{ filteredDocuments.length }} document{{ filteredDocuments.length !== 1 ? 's' : '' }}
          </span>

          <Button
            variant="ghost"
            @click="confirmDeleteAll"
            :disabled="filteredDocuments.length === 0 || isBusy"
          >
            <i class="dku-icon-trash-16"></i>
            Delete all
          </Button>
        </div>

        <!-- Document List -->
        <div class="flex-1 overflow-y-auto space-y-2">
          <div v-if="filteredDocuments.length === 0" class="text-center py-8 text-gray-500">
            <div v-if="isBusy" class="flex flex-col items-center space-y-2">
              <div class="flex items-center space-x-2">
                <div class="animate-spin rounded-full h-4 w-4 border-b-2 border-primary"></div>
                <span>Processing documents...</span>
              </div>
              <p class="text-sm">Documents will appear here once processing is complete</p>
            </div>
            <p v-else>No documents found</p>
          </div>

          <div
            v-for="doc in filteredDocuments"
            :key="doc.name"
            class="p-3 bg-white rounded-lg border border-border"
          >
            <div class="flex items-start justify-between">
              <div class="min-w-0 flex-1">
                <div class="flex items-center">
                  <i v-if="getFileIcon(doc.name)" :class="getFileIcon(doc.name)" class="mr-2"></i>
                  <p class="font-medium text-sm truncate">{{ doc.name }}</p>
                </div>
                <div class="flex items-center space-x-2 mt-1">
                  <i class="dku-icon-data-size-weight-12 mr-2"></i>
                  <span class="text-xs text-muted-foreground">
                    {{ doc.size ? formatFileSize(doc.size) : 'N/A' }}
                  </span>
                  <Badge
                    variant="secondary"
                    class="text-xs"
                    :class="{
                      'bg-yellow-100 text-yellow-700': !doc.active,
                      'bg-red-100 text-red-700': doc.deletePending,
                      'text-[#0B590B] bg-[#E7EEE7]': doc.active,
                    }"
                  >
                    {{ getDocStatus(doc) }}
                  </Badge>
                </div>
              </div>

              <Button
                v-if="!doc.deletePending && !isBusy"
                size="icon"
                variant="ghost"
                class="shrink-0 h-8 w-8"
                @click.stop="confirmDelete(doc)"
              >
                <i class="dku-icon-trash-16"></i>
              </Button>
            </div>
          </div>
        </div>
      </div>
    </div>

    <!-- DELETE ALL CONFIRMATION DIALOG -->
    <AlertDialog v-model:open="showDeleteAllConfirm">
      <AlertDialogContent>
        <AlertDialogHeader>
          <AlertDialogTitle>
            <i class="dku-icon-trash-16"></i> Delete
            {{ filteredDocuments.filter((doc) => !doc.deletePending).length }}
            documents</AlertDialogTitle
          >
          <AlertDialogDescription>
            {{
              searchQuery
                ? 'You are about to delete all filtered documents'
                : 'You are about to delete all the documents'
            }}
            You can't undo this action later
          </AlertDialogDescription>
        </AlertDialogHeader>
        <AlertDialogFooter>
          <AlertDialogCancel>Cancel</AlertDialogCancel>
          <AlertDialogAction class="bg-red-600 text-white hover:bg-red-700" @click="doDeleteAll">
            Delete All
          </AlertDialogAction>
        </AlertDialogFooter>
      </AlertDialogContent>
    </AlertDialog>
  </div>
</template>

<script setup lang="ts">
import { ref, computed, onMounted, onBeforeUnmount, watch } from 'vue'
import { useSidebar } from '../../ui/sidebar'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select'
import { toast } from 'vue-sonner'
import { filesExtensions, type DocumentItem } from '@/types/document'
import {
  AlertDialog,
  AlertDialogContent,
  AlertDialogHeader,
  AlertDialogTitle,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogCancel,
  AlertDialogAction,
} from '@/components/ui/alert-dialog'
import { Badge } from '@/components/ui/badge'
import { API } from '@/Api'
import { useIndexingStore } from '@/store/indexingStore'
import { isIndexing } from '@/constants/indexing'
import { useAgentStore, type DocEntry } from '@/store/agentStore'
import { DOC_DELETE_FAILED, ERROR_409, UPLOAD_FAILED } from '@/constants/notifs'
import { Loader2 } from 'lucide-vue-next'
// Props
const props = defineProps<{
  // documents?: any[]
  isIndexingBusy: boolean
  agentId: string
  agentName: string
}>()
const agentStore = useAgentStore()
const { draft } = agentStore
watch(
  () => draft.documents,
  () => {
    filesToUpload.value = draft.documents?.filter((d) => d instanceof File) ?? []
  }
)
const docs = computed(() => draft.documents ?? [])
const noActiveDocs = computed(
  () =>
    draft.documents?.filter(
      (d: DocEntry) => !(d instanceof File) && !d.active && !d.deletePending
    ) ?? []
)
// Emits
const emit = defineEmits<{
  (e: 'confirm-delete', doc: DocumentItem): void
  (e: 'delete-docs', docs: string[]): void
}>()
// State
const searchQuery = ref('')
const sortBy = ref('name') // Default sort by "Name"
const isDragging = ref(false)
const showDeleteAllConfirm = ref(false)
const dragCounter = ref(0)

// Mobile detection
const { isDesktop } = useSidebar()

// File input ref
const fileInput = ref<HTMLInputElement | null>(null)

// Get file type icon based on file extension
function getFileIcon(filename: string): string | null {
  const extension = filename.toLowerCase().split('.').pop()
  switch (extension) {
    case 'pdf':
      return 'dku-icon-file-12'
    case 'jpg':
    case 'jpeg':
    case 'png':
      return 'dku-icon-image-12'
    case 'txt':
      return 'dku-icon-file-text-12'
    default:
      return null
  }
}
// Computed
const normalizedDocs = computed((): DocumentItem[] => {
  return (draft.documents ?? []).map((d: DocEntry): DocumentItem => {
    if (d instanceof File) {
      // File path: no custom flags on File itself
      return {
        name: d.name,
        size: d.size,
        active: false,
        deletePending: false,
      }
    } else {
      // Object-with-flags path
      return {
        name: d.name,
        active: d.active ?? false,
        deletePending: d.deletePending,
        size: d.size ?? undefined,
      }
    }
  })
})
const filteredDocuments = computed(() => {
  let docs = [...normalizedDocs.value]
  // Filter by search
  if (searchQuery.value) {
    const query = searchQuery.value.toLowerCase()
    docs = docs.filter((d) => d.name.toLowerCase().includes(query))
  }

  // Sort
  docs.sort((a, b) => {
    switch (sortBy.value) {
      case 'name':
        return a.name.localeCompare(b.name)
      case 'size':
        // Sorts smallest first (ascending). Treats undefined size as 0.
        return (a.size || 0) - (b.size || 0)
      default:
        return 0
    }
  })

  return docs
})

// Methods
function getDocStatus(doc: DocumentItem): string {
  if (doc.deletePending) return 'Pending Deletion'
  if (!doc.active && !isBusy.value) return 'Pending processing'
  if (!doc.active && isBusy.value) return 'Processing'
  return 'Active'
}

function formatFileSize(bytes: number): string {
  if (!Number.isFinite(bytes) || bytes < 0) return 'Invalid size'
  if (bytes === 0) return '0 B'

  const k = 1024
  const sizes = ['B', 'KB', 'MB', 'GB']
  let i = Math.floor(Math.log(bytes) / Math.log(k))
  i = Math.max(0, Math.min(i, sizes.length - 1))
  return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i]
}

function confirmDelete(doc: DocumentItem) {
  emit('confirm-delete', doc) // TODO fix typing
}

function confirmDeleteAll() {
  if (filteredDocuments.value.length === 0 || isBusy.value) return
  showDeleteAllConfirm.value = true
}

async function doDeleteAll() {
  showDeleteAllConfirm.value = false
  const documentsToDelete = filteredDocuments.value.filter((doc) => !doc.deletePending)
  filesToUpload.value = []
  try {
    const filenames = documentsToDelete.map((doc) => doc.name)
    if (filenames.length === 0) {
      return
    }
    const { data } = await API.deleteAgentDocuments(props.agentId, filenames)
    agentStore.draft.documents = data.documents
    // Update indexing store and broadcast to other components
    const idxStore = useIndexingStore()
    idxStore.track(props.agentId, data.indexing, props.agentName)
    isBusy.value = true
    agentStore.draft.indexing = data.indexing
    window.dispatchEvent(
      new CustomEvent('docs-updated', {
        detail: { agentId: props.agentId, docs: data.documents, indexing: data.indexing },
      })
    )
    if (data.indexing) {
      toast.success(
        `${filenames.length} document${
          filenames.length > 1 ? 's' : ''
        } queued for deletion and re-processing`
      )
    } else {
      toast.success(`${filenames.length} document${filenames.length > 1 ? 's' : ''} deleted`)
    }
  } catch (err: any) {
    toast.error(err.response?.status === 409 ? ERROR_409 : DOC_DELETE_FAILED)
  }
}
const isBusy = ref(draft.indexing ? isIndexing(draft.indexing?.status) : props.isIndexingBusy)
watch(
  () => draft.indexing,
  (newVal: any) => {
    isBusy.value = isIndexing(newVal?.status)
  },
  { deep: true }
)

function handleDragEnter() {
  dragCounter.value++
  isDragging.value = true
}

function handleDragLeave() {
  dragCounter.value--
  if (dragCounter.value <= 0) {
    isDragging.value = false
  }
}

function handleDrop(e: DragEvent) {
  dragCounter.value = 0
  isDragging.value = false
  if (uploadDisabled.value || isBusy.value) return

  const files = Array.from(e.dataTransfer?.files || [])
  if (files.length) {
    addFilesToUpload(files)
  }
}
const filesToUpload = ref<any>(draft.documents?.filter((d) => d instanceof File) ?? [])
const uploadDisabled = computed(() => !draft.project_key)

async function launchIndexing() {
  try {
    const { data } = await API.indexAgentDocuments(props.agentId)
    agentStore.draft.documents = data.documents
    // Update indexing store and broadcast to other components
    const idxStore = useIndexingStore()
    idxStore.track(props.agentId, data.indexing, props.agentName)
    isBusy.value = true
    agentStore.draft.indexing = data.indexing
    window.dispatchEvent(
      new CustomEvent('docs-updated', {
        detail: { agentId: props.agentId, docs: data.documents, indexing: data.indexing },
      })
    )
    const multiple = noActiveDocs.value.length > 1 ? 's' : ''
    toast.success(`${noActiveDocs.value.length} document${multiple} queued for processing`)
  } catch (err: any) {
    toast.error(err.response?.status === 409 ? ERROR_409 : UPLOAD_FAILED)
  }
}
// Upload
async function addFilesToUpload(files: File[]) {
  if (!files.length) return

  const allowedExtensions = filesExtensions.split(',').map((ext) => ext.trim())
  const validFiles = []
  const invalidFiles = []

  for (const file of files) {
    const extension = ('.' + file.name.split('.').pop()?.toLowerCase()) as string
    if (allowedExtensions.includes(extension)) {
      validFiles.push(file)
    } else {
      invalidFiles.push(file)
    }
  }

  if (invalidFiles.length > 0) {
    const rejectedExtensions = [
      ...new Set(invalidFiles.map((f) => '.' + f.name.split('.').pop()?.toLowerCase())),
    ]
    toast.warning(
      `${
        invalidFiles.length
      } file(s) were ignored due to unsupported file type(s): ${rejectedExtensions.join(
        ', '
      )}. Supported types: ${allowedExtensions.join(', ')}`
    )
  }

  if (validFiles.length === 0) {
    return
  }

  const existing = new Set(docs.value.map((d) => (typeof d === 'string' ? d : d.name)))
  const toSave = validFiles.filter((f) => !existing.has(f.name))

  if (toSave.length === 0) {
    if (invalidFiles.length === 0) {
      toast.info('Selected document(s) already uploaded for this agent')
    }
    return
  }
  try {
    draft.documents.push(...toSave)
    const { data } = await API.uploadAgentDocument(props.agentId, toSave)
    filesToUpload.value = []
    useAgentStore().draft.documents = data.documents
    window.dispatchEvent(
      new CustomEvent('docs-updated', {
        detail: { agentId: props.agentId, docs: docs.value, indexing: 'pending' },
      })
    )
    toast.success(`${toSave.length} Document${toSave.length > 1 ? 's' : ''} uploaded successfully`)
  } catch (err: any) {
    toast.error(err.response?.status === 409 ? ERROR_409 : UPLOAD_FAILED)
  }
}

function handleFileSelection(event: Event) {
  const target = event.target as HTMLInputElement
  if (target.files) {
    const files = Array.from(target.files)
    if (files.length) {
      addFilesToUpload(files)
    }
  }
  // Reset the input value to allow uploading the same file again
  if (target) {
    target.value = ''
  }
}

// Sync across components
function handleDocsUpdated(e: any) {
  const d = e.detail
  if (!d || d.agentId !== props.agentId) return
  agentStore.draft.documents = d.docs
  agentStore.draft.indexing = d.indexing
  isBusy.value = isIndexing(d.indexing?.status)
}

onMounted(() => window.addEventListener('docs-updated', handleDocsUpdated))
onBeforeUnmount(() => window.removeEventListener('docs-updated', handleDocsUpdated))
</script>
