"use client"

import { toastApiError, errorMessageOr } from "@/lib/toast-api-error";
import * as React from "react"
import { Eye, Loader2, Plus, RefreshCw, RotateCcw, SearchCheck, Trash2, Upload } from "lucide-react"
import Link from "next/link"
import { useRouter } from "next/navigation"
import { toast } from "sonner"

import { FormPageHeader } from "@/app/dashboard/_components/form"
import {
  deleteDeepdexUploadQueueItem,
  fetchDeepdexUploadQueue,
  getDeepdexUploadQueueReviewUrl,
  getDeepdexUploadQueueViewUrl,
  publishDeepdexUploadQueueItem,
  reprocessDeepdexUploadQueueViaKosr,
} from "@/components/deepdex/api/deepdex.api"
import { DeepdexUploadForm } from "@/components/deepdex/create/deepdex-upload-form"
import { getDeepdexDocTypeLabel } from "@/components/deepdex/data/deepdex-doc-types"
import type { DeepdexUploadQueueItem } from "@/components/deepdex/types"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Checkbox } from "@/components/ui/checkbox"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import {
  Sheet,
  SheetContent,
  SheetDescription,
  SheetHeader,
  SheetTitle,
  SheetTrigger,
} from "@/components/ui/sheet"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"

function statusBadgeVariant(status: string): "default" | "secondary" | "destructive" | "outline" {
  if (status === "failed") return "destructive"
  if (status === "pending_review") return "default"
  if (status === "completed") return "secondary"
  return "outline"
}

function isBulkReviewEligible(item: DeepdexUploadQueueItem, module: "deepdex" | "structure" | "accumulator"): boolean {
  if (module !== "structure" && module !== "accumulator") return false
  if (item.moduleType !== module) return false
  if (item.canReview) return true
  return item.status === "pending_review" || item.status === "completed"
}

function isFailedItem(item: DeepdexUploadQueueItem): boolean {
  return item.status === "failed"
}

function isSelectableItem(item: DeepdexUploadQueueItem, module: "deepdex" | "structure" | "accumulator"): boolean {
  return isBulkReviewEligible(item, module) || isFailedItem(item)
}

async function sleep(ms: number): Promise<void> {
  await new Promise((resolve) => {
    window.setTimeout(resolve, ms)
  })
}

export function DeepdexUploadQueuePage() {
  const router = useRouter()
  const [items, setItems] = React.useState<DeepdexUploadQueueItem[]>([])
  const [module, setModule] = React.useState<"deepdex" | "structure" | "accumulator">("deepdex")
  const [selectedIds, setSelectedIds] = React.useState<string[]>([])
  const [isLoading, setIsLoading] = React.useState(true)
  const [isRefreshing, setIsRefreshing] = React.useState(false)
  const [busyId, setBusyId] = React.useState<string | null>(null)
  const [isRetryingProcessing, setIsRetryingProcessing] = React.useState(false)
  const [isUploadOpen, setIsUploadOpen] = React.useState(false)

  const showBulkReview = module === "structure" || module === "accumulator"
  const processingItems = items.filter((item) => item.status === "processing")
  const hasProcessingItems = processingItems.length > 0
  const failedItems = items.filter(isFailedItem)
  const hasFailedItems = failedItems.length > 0
  const isQueueBusy = isRetryingProcessing || hasProcessingItems
  const showRetryProcessing = hasFailedItems || isQueueBusy
  const showSelectColumn = showBulkReview || hasFailedItems || isQueueBusy
  const selectableItems = items.filter((item) => isSelectableItem(item, module))
  const selectedFailedIds = selectedIds.filter((id) => failedItems.some((item) => item.id === id))
  const failedIdsKey = failedItems.map((item) => item.id).join(",")
  const allSelectableSelected =
    selectableItems.length > 0 && selectableItems.every((item) => selectedIds.includes(item.id))
  const someSelectableSelected = selectableItems.some((item) => selectedIds.includes(item.id))

  const load = React.useCallback(async (activeModule: "deepdex" | "structure" | "accumulator") => {
    try {
      const data = await fetchDeepdexUploadQueue({
        page: 1,
        pageSize: 50,
        filters: { module: activeModule },
      })
      setItems(data.items)
      return data.items
    } catch (error) {
      const message = errorMessageOr(error, "Failed to load upload queue")
      toast.error(message)
      return null
    }
  }, [])

  React.useEffect(() => {
    void (async () => {
      setIsLoading(true)
      await load(module)
      setIsLoading(false)
    })()
  }, [load, module])

  // Poll while the queue is busy (in-flight retry or rows still processing after refresh).
  React.useEffect(() => {
    if (!isQueueBusy) return

    const timer = window.setInterval(() => {
      void load(module)
    }, 2000)

    return () => {
      window.clearInterval(timer)
    }
  }, [isQueueBusy, load, module])

  // Drop selections that are no longer failed (e.g. claimed as processing).
  React.useEffect(() => {
    if (!hasProcessingItems) return
    const failedIdSet = new Set(failedIdsKey ? failedIdsKey.split(",") : [])
    setSelectedIds((prev) => {
      const next = prev.filter((id) => failedIdSet.has(id))
      return next.length === prev.length ? prev : next
    })
  }, [hasProcessingItems, failedIdsKey])

  const refresh = React.useCallback(async () => {
    setIsRefreshing(true)
    await load(module)
    setIsRefreshing(false)
  }, [load, module])

  const waitUntilProcessingClears = React.useCallback(
    async (activeModule: "deepdex" | "structure" | "accumulator") => {
      for (let attempt = 0; attempt < 150; attempt++) {
        const latest = await load(activeModule)
        if (!latest?.some((item) => item.status === "processing")) {
          return
        }
        await sleep(2000)
      }
    },
    [load],
  )

  const handleDelete = async (id: string) => {
    setBusyId(id)
    try {
      await deleteDeepdexUploadQueueItem(id)
      toast.success("Queue item deleted")
      await load(module)
    } catch (error) {
      toastApiError(error, "Delete failed")
    } finally {
      setBusyId(null)
    }
  }

  const handleBulkReview = () => {
    if (!showBulkReview) return
    const ids = items
      .filter((item) => selectedIds.includes(item.id) && isBulkReviewEligible(item, module))
      .map((item) => item.id)
    if (ids.length === 0) {
      toast.error(
        module === "structure"
          ? "Please select at least one structure item."
          : "Please select at least one accumulator item.",
      )
      return
    }
    router.push(getDeepdexUploadQueueReviewUrl(module, ids))
  }

  const handleRetryProcessing = async () => {
    if (hasProcessingItems) {
      toast.error("Wait until current processing finishes before retrying again.")
      return
    }

    const ids = selectedFailedIds
    if (ids.length === 0) {
      toast.error("Select at least one failed file to retry.")
      return
    }

    setIsRetryingProcessing(true)

    try {
      const result = await reprocessDeepdexUploadQueueViaKosr(ids)
      await waitUntilProcessingClears(module)

      if (result.failed > 0 && result.succeeded === 0) {
        toast.error(result.errors[0] ?? result.message)
      } else if (result.failed > 0) {
        toast.warning(result.message)
      } else {
        toast.success(result.message)
      }
      setSelectedIds((prev) => prev.filter((id) => !ids.includes(id)))
      await load(module)
    } catch (error) {
      // Request may have timed out while the server kept working.
      await waitUntilProcessingClears(module)
      toastApiError(error, "Retry processing failed")
      await load(module)
    } finally {
      setIsRetryingProcessing(false)
    }
  }

  const handlePublish = async (id: string) => {
    setBusyId(id)
    try {
      const message = await publishDeepdexUploadQueueItem(id)
      toast.success(message)
      await load(module)
    } catch (error) {
      toastApiError(error, "Publish failed")
    } finally {
      setBusyId(null)
    }
  }

  return (
    <div className="flex flex-col">
      <FormPageHeader
        backHref="/dashboard/deepdex/view-all"
        parentLabel="Deepdex"
        currentLabel="Upload queue"
        titleIcon={<Upload className="size-4 text-primary" />}
        title="Upload queue"
        description="Upload files, review queue status, then publish processed items to Deepdex."
      />
      <Card className="mt-6">
        <CardHeader className="flex flex-row items-center justify-between">
          <CardTitle>Queue items</CardTitle>
          <div className="flex items-center gap-2">
            {showBulkReview ? (
              <Button
                size="sm"
                onClick={handleBulkReview}
                disabled={isQueueBusy}
                title={
                  module === "structure"
                    ? "Review selected structure files"
                    : "Review selected accumulator files"
                }
              >
                <SearchCheck className="size-4" />
                Bulk review
              </Button>
            ) : null}
            {showRetryProcessing ? (
              <Button
                size="sm"
                variant="outline"
                onClick={() => void handleRetryProcessing()}
                disabled={isQueueBusy || selectedFailedIds.length === 0}
                title={
                  hasProcessingItems
                    ? "Processing in progress — wait until it finishes"
                    : "Retry processing for selected failed files"
                }
              >
                {isQueueBusy ? (
                  <Loader2 className="size-4 animate-spin" />
                ) : (
                  <RotateCcw className="size-4" />
                )}
                {isQueueBusy ? "Processing…" : "Retry processing"}
              </Button>
            ) : null}
            <Sheet open={isUploadOpen} onOpenChange={setIsUploadOpen}>
              <SheetTrigger asChild>
                <Button size="sm" disabled={isQueueBusy}>
                  <Plus className="size-4" />
                  Add files
                </Button>
              </SheetTrigger>
              <SheetContent
                side="right"
                className="h-screen w-1/2 overflow-y-auto max-w-none border-0 data-[side=right]:w-1/2 data-[side=right]:sm:max-w-none"
              >
                <SheetHeader>
                  <SheetTitle>Upload files</SheetTitle>
                  <SheetDescription>Add files to the processing queue.</SheetDescription>
                </SheetHeader>
                <div className="p-4">
                  <DeepdexUploadForm
                    module={module}
                    onUploaded={() => {
                      setIsUploadOpen(false)
                      void refresh()
                    }}
                  />
                </div>
              </SheetContent>
            </Sheet>
            <Button
              variant="outline"
              size="sm"
              onClick={() => void refresh()}
              disabled={isRefreshing || isRetryingProcessing}
            >
              {isRefreshing ? <Loader2 className="size-4 animate-spin" /> : <RefreshCw className="size-4" />}
              Refresh
            </Button>
          </div>
        </CardHeader>
        <CardContent className="space-y-4 p-0">
          <div className="px-4 pt-4">
            <Tabs
              value={module}
              onValueChange={(value) => {
                if (isQueueBusy) return
                setSelectedIds([])
                setModule(value as typeof module)
              }}
            >
              <TabsList>
                <TabsTrigger value="deepdex" disabled={isQueueBusy}>
                  Deepdex
                </TabsTrigger>
                <TabsTrigger value="structure" disabled={isQueueBusy}>
                  Structure
                </TabsTrigger>
                <TabsTrigger value="accumulator" disabled={isQueueBusy}>
                  Accumulator
                </TabsTrigger>
              </TabsList>
            </Tabs>
            {showBulkReview || hasFailedItems || isQueueBusy ? (
              <p className="mt-2 text-sm text-muted-foreground">
                {isQueueBusy
                  ? "Processing in progress… Retry stays locked until every in-progress file finishes."
                  : showBulkReview && hasFailedItems
                    ? "Select rows for Bulk review, or select failed files to Retry processing."
                    : showBulkReview
                      ? "Select rows and use Bulk review."
                      : "Select failed files and use Retry processing."}
              </p>
            ) : null}
          </div>
          {isLoading ? (
            <div className="p-4 text-sm text-muted-foreground">Loading queue…</div>
          ) : items.length === 0 ? (
            <div className="p-4 text-sm text-muted-foreground">Queue is empty.</div>
          ) : (
            <Table>
              <TableHeader>
                <TableRow>
                  {showSelectColumn ? (
                    <TableHead className="w-10">
                      <Checkbox
                        aria-label="Select all"
                        disabled={selectableItems.length === 0 || hasProcessingItems}
                        checked={
                          allSelectableSelected ? true : someSelectableSelected ? "indeterminate" : false
                        }
                        onCheckedChange={(checked) => {
                          if (hasProcessingItems) return
                          setSelectedIds(checked === true ? selectableItems.map((item) => item.id) : [])
                        }}
                      />
                    </TableHead>
                  ) : null}
                  <TableHead>ID</TableHead>
                  <TableHead>File name</TableHead>
                  <TableHead>Module</TableHead>
                  <TableHead>Status</TableHead>
                  <TableHead>Doc type</TableHead>
                  <TableHead>Created</TableHead>
                  <TableHead className="text-right">Actions</TableHead>
                </TableRow>
              </TableHeader>
              <TableBody>
                {items.map((item) => (
                  <TableRow key={item.id}>
                    {showSelectColumn ? (
                      <TableCell>
                        {isSelectableItem(item, module) ? (
                          <Checkbox
                            aria-label={`Select queue item ${item.id}`}
                            checked={selectedIds.includes(item.id)}
                            disabled={hasProcessingItems && isFailedItem(item)}
                            onCheckedChange={(checked) => {
                              if (hasProcessingItems && isFailedItem(item)) return
                              setSelectedIds((prev) =>
                                checked === true
                                  ? prev.includes(item.id) ? prev : [...prev, item.id]
                                  : prev.filter((id) => id !== item.id),
                              )
                            }}
                          />
                        ) : null}
                      </TableCell>
                    ) : null}
                    <TableCell>{item.id}</TableCell>
                    <TableCell className="max-w-[18rem] truncate" title={item.originalName}>
                      {item.originalName}
                      {item.errorMessage ? (
                        <p className="mt-1 text-xs text-destructive">{item.errorMessage}</p>
                      ) : null}
                    </TableCell>
                    <TableCell className="capitalize">{item.moduleType}</TableCell>
                    <TableCell>
                      <Badge variant={statusBadgeVariant(item.status)}>{item.status}</Badge>
                    </TableCell>
                    <TableCell>{getDeepdexDocTypeLabel(item.docType)}</TableCell>
                    <TableCell>{item.createdAt || "—"}</TableCell>
                    <TableCell className="text-right">
                      <div className="flex justify-end gap-2">
                        <Button variant="outline" size="sm" asChild>
                          <Link href={getDeepdexUploadQueueViewUrl(item.viewToken)} target="_blank">
                            <Eye className="size-4" />
                          </Link>
                        </Button>
                        {(module === "structure" || module === "accumulator") ? (
                          <Button variant="outline" size="sm" asChild>
                            <Link
                              href={getDeepdexUploadQueueReviewUrl(module, item.id)}
                              target="_blank"
                            >
                              <SearchCheck className="size-4" />
                            </Link>
                          </Button>
                        ) : null}
                        <Button
                          variant="default"
                          size="sm"
                          onClick={() => void handlePublish(item.id)}
                          disabled={module !== "deepdex" || !item.canPublish || busyId === item.id}
                        >
                          {busyId === item.id ? <Loader2 className="size-4 animate-spin" /> : null}
                          Publish
                        </Button>
                        <Button
                          variant="outline"
                          size="sm"
                          onClick={() => void handleDelete(item.id)}
                          disabled={!item.canDelete || busyId === item.id}
                        >
                          <Trash2 className="size-4" />
                        </Button>
                      </div>
                    </TableCell>
                  </TableRow>
                ))}
              </TableBody>
            </Table>
          )}
        </CardContent>
      </Card>
    </div>
  )
}
