"use client"

import { isApiSuccess } from "@/lib/api-messages";
import { toastApiError } from "@/lib/toast-api-error";
import { useRouter } from "next/navigation"
import { AlertTriangle, CheckCircle2, FolderUp, Loader2, XCircle } from "lucide-react"
import * as React from "react"
import { toast } from "sonner"

import { FormPanel } from "@/app/dashboard/_components/form"
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogMedia,
  AlertDialogTitle,
} from "@/components/ui/alert-dialog"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Field, FieldLabel } from "@/components/ui/field"
import { SettingsSelect } from "@/components/settings/ui/settings-select"
import type { SettingsSelectOption } from "@/components/settings/types"
import { cn } from "@/lib/utils"
import {
  createCubFolderUpload,
  fetchCubFolderMeta,
  type CubFolderUploadFileResult,
} from "@/components/xml-apis/upload-xml/api/upload-xml.api"
import {
  buildCubFolderPlan,
  CUB_CATEGORY_LABELS,
  CUB_FOLDER_CATEGORIES,
  type CubFolderCategory,
  type CubFolderPlan,
} from "@/components/xml-apis/upload-xml/create/cub-folder-classifier"
import { readDroppedSelection } from "@/components/xml-apis/upload-xml/create/read-dropped-files"

const listHref = "/dashboard/xml-apis/upload-xml"

const PLEASE_SELECT: SettingsSelectOption = { value: "please-select", label: "Please Select" }

export function CubFolderUploadPanel() {
  const router = useRouter()
  const inputRef = React.useRef<HTMLInputElement>(null)

  const [bankOptions, setBankOptions] = React.useState<SettingsSelectOption[]>([PLEASE_SELECT])
  const [bankApiId, setBankApiId] = React.useState("please-select")
  const [plan, setPlan] = React.useState<CubFolderPlan | null>(null)
  const [allowPartial, setAllowPartial] = React.useState(false)
  const [isSubmitting, setIsSubmitting] = React.useState(false)
  const [results, setResults] = React.useState<CubFolderUploadFileResult[] | null>(null)
  const [isDragging, setIsDragging] = React.useState(false)
  const [pending, setPending] = React.useState<{ files: File[]; folderName: string | null } | null>(
    null
  )

  React.useEffect(() => {
    const controller = new AbortController()
    void fetchCubFolderMeta({ signal: controller.signal })
      .then((meta) => {
        setBankOptions([
          PLEASE_SELECT,
          ...meta.bankOptions.map((option) => ({ value: option.value, label: option.label })),
        ])
      })
      .catch((error) => {
        // Ignore aborts caused by React StrictMode / component unmount.
        if (controller.signal.aborted || (error instanceof DOMException && error.name === "AbortError")) {
          return
        }
        toast.error("Failed to load CUB banks")
      })
    return () => controller.abort()
  }, [])

  // Present a selection (from drop or browse) for confirmation in our own popup.
  const presentSelection = (files: File[], folderName: string | null) => {
    setResults(null)
    if (!files.length) return
    setPending({ files, folderName })
  }

  const handleDrop = async (event: React.DragEvent<HTMLDivElement>) => {
    event.preventDefault()
    setIsDragging(false)
    const { files, folderName } = await readDroppedSelection(event.dataTransfer)
    presentSelection(files, folderName)
  }

  const handleBrowse = (fileList: FileList | null) => {
    if (!fileList?.length) return
    const files = Array.from(fileList)
    const relativePath = files[0]?.webkitRelativePath ?? ""
    const folderName = relativePath.includes("/") ? relativePath.split("/")[0] : null
    presentSelection(files, folderName)
    if (inputRef.current) inputRef.current.value = ""
  }

  const confirmSelection = () => {
    if (!pending) return
    const xmlFiles = pending.files.filter((file) => file.name.toLowerCase().endsWith(".xml"))
    setPlan(buildCubFolderPlan(xmlFiles))
    setPending(null)
  }

  const collectFilesToSubmit = (): File[] => {
    if (!plan) return []
    return CUB_FOLDER_CATEGORIES.map((category) => plan.matched[category]).filter(
      (file): file is File => Boolean(file)
    )
  }

  const canSubmit =
    bankApiId !== "please-select" &&
    plan !== null &&
    Object.keys(plan.matched).length > 0 &&
    (allowPartial || plan.missing.length === 0)

  const onSubmit = async () => {
    if (bankApiId === "please-select") {
      toast.error("Select a CUB bank")
      return
    }
    const files = collectFilesToSubmit()
    if (!files.length) {
      toast.error("No recognised CUB files to upload")
      return
    }

    setIsSubmitting(true)
    setResults(null)
    try {
      const response = await createCubFolderUpload({ bankApiId, files, allowPartial })
      setResults(response.data?.results ?? [])

      if (isApiSuccess(response)) {
        toast.success(response.message ?? "CUB folder uploaded successfully")
        router.push(listHref)
        return
      }

      toast.error(response.message ?? "Some files could not be processed")
    } catch (error) {
      toastApiError(error, "Failed to upload CUB folder")
    } finally {
      setIsSubmitting(false)
    }
  }

  const resetSelection = () => {
    setPlan(null)
    setResults(null)
    if (inputRef.current) inputRef.current.value = ""
  }

  return (
    <FormPanel title="CUB folder upload">
      <div className="space-y-6">
        <Alert>
          <FolderUp className="size-4" />
          <AlertTitle>Upload the whole export folder</AlertTitle>
          <AlertDescription>
            Pick the CUB export folder — the five relevant files (Positions, Transactions,
            Assets, Exchange rates, Accounts) are detected automatically by their file names.
            Everything else is ignored.
          </AlertDescription>
        </Alert>

        <Field className="max-w-md gap-1.5">
          <FieldLabel className="text-sm font-medium">
            CUB Bank <span className="text-destructive">*</span>
          </FieldLabel>
          <SettingsSelect
            value={bankApiId}
            onValueChange={setBankApiId}
            options={bankOptions}
            placeholder="Please select"
          />
        </Field>

        <Field className="gap-1.5">
          <FieldLabel className="text-sm font-medium">Export folder</FieldLabel>
          <div
            role="button"
            tabIndex={0}
            onClick={() => inputRef.current?.click()}
            onKeyDown={(event) => {
              if (event.key === "Enter" || event.key === " ") inputRef.current?.click()
            }}
            onDragOver={(event) => {
              event.preventDefault()
              setIsDragging(true)
            }}
            onDragLeave={() => setIsDragging(false)}
            onDrop={(event) => void handleDrop(event)}
            className={cn(
              "flex min-h-[180px] cursor-pointer flex-col items-center justify-center gap-3 rounded-lg border-2 border-dashed px-6 py-10 transition-colors",
              isDragging
                ? "border-primary bg-primary/5"
                : "border-muted-foreground/30 bg-muted/20 hover:border-primary/50"
            )}
          >
            <input
              ref={inputRef}
              type="file"
              multiple
              accept=".xml,application/xml,text/xml"
              className="sr-only"
              onChange={(event) => handleBrowse(event.target.files)}
            />
            <div className="flex size-14 items-center justify-center rounded-lg bg-primary text-primary-foreground">
              <FolderUp className="size-7" />
            </div>
            <span className="text-sm font-medium text-primary">
              Drag the export folder here, or browse files
            </span>
            <p className="text-xs text-muted-foreground">
              Drop the whole CUB folder (recommended) or select the XML files manually
            </p>
          </div>
        </Field>

        {plan ? (
          <CubFolderPlanPreview plan={plan} results={results} onReset={resetSelection} />
        ) : null}

        <label className="flex items-center gap-2 text-sm text-muted-foreground">
          <input
            type="checkbox"
            checked={allowPartial}
            onChange={(event) => setAllowPartial(event.target.checked)}
            className="size-4 rounded border-muted-foreground/40"
          />
          Upload even if some of the five files are missing
        </label>

        <div className="flex items-center gap-2">
          <Button type="button" onClick={() => void onSubmit()} disabled={!canSubmit || isSubmitting}>
            {isSubmitting ? <Loader2 className="size-4 animate-spin" /> : <FolderUp className="size-4" />}
            {isSubmitting ? "Uploading…" : "Upload folder"}
          </Button>
          <Button type="button" variant="outline" asChild>
            <a href={listHref}>Cancel</a>
          </Button>
        </div>
      </div>

      <AlertDialog
        open={pending !== null}
        onOpenChange={(open) => {
          if (!open) setPending(null)
        }}
      >
        <AlertDialogContent className="sm:max-w-md">
          <AlertDialogHeader>
            <AlertDialogMedia className="bg-primary/10 text-primary">
              <FolderUp className="size-5" />
            </AlertDialogMedia>
            <AlertDialogTitle>
              {pending?.folderName
                ? `Use ${pending.files.length} files from “${pending.folderName}”?`
                : `Use ${pending?.files.length ?? 0} selected files?`}
            </AlertDialogTitle>
            <AlertDialogDescription>
              We&apos;ll scan the selection and automatically pick the five CUB files
              (Positions, Transactions, Assets, Exchange rates, Accounts). Everything else is
              ignored — nothing is uploaded until you confirm on the next step.
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel>Cancel</AlertDialogCancel>
            <AlertDialogAction
              onClick={(event) => {
                event.preventDefault()
                confirmSelection()
              }}
            >
              Continue
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>
    </FormPanel>
  )
}

function flattenMessages(source: Record<string, string[]> | undefined): string[] {
  if (!source) return []
  return Object.values(source).flat()
}

function CubFolderPlanPreview({
  plan,
  results,
  onReset,
}: {
  plan: CubFolderPlan
  results: CubFolderUploadFileResult[] | null
  onReset: () => void
}) {
  const resultByCategory = new Map<string, CubFolderUploadFileResult>()
  for (const result of results ?? []) {
    resultByCategory.set(result.category, result)
  }

  return (
    <div className="space-y-4">
      <div className="overflow-hidden rounded-lg border">
        <table className="w-full text-sm">
          <thead className="bg-muted/40 text-left text-xs uppercase text-muted-foreground">
            <tr>
              <th className="px-3 py-2 font-medium">Type</th>
              <th className="px-3 py-2 font-medium">Detected file</th>
              <th className="px-3 py-2 font-medium">Status</th>
            </tr>
          </thead>
          <tbody>
            {CUB_FOLDER_CATEGORIES.map((category) => {
              const file = plan.matched[category as CubFolderCategory]
              const result = resultByCategory.get(category)
              const messages = [
                ...flattenMessages(result?.errors),
                ...flattenMessages(result?.warnings),
              ]
              return (
                <tr key={category} className="border-t align-top">
                  <td className="px-3 py-2 font-medium">{CUB_CATEGORY_LABELS[category]}</td>
                  <td className="px-3 py-2 text-muted-foreground">
                    {file ? file.name : <span className="italic">Not found</span>}
                    {messages.length > 0 ? (
                      <ul className="mt-1 list-disc space-y-0.5 pl-4 text-xs text-destructive">
                        {messages.map((message, index) => (
                          <li key={index}>{message}</li>
                        ))}
                      </ul>
                    ) : null}
                    {result && isApiSuccess(result) && typeof result.rows === "number" ? (
                      <p className="mt-1 text-xs text-muted-foreground">{result.rows} rows imported</p>
                    ) : null}
                  </td>
                  <td className="px-3 py-2">
                    <CategoryStatusBadge hasFile={Boolean(file)} result={result} />
                  </td>
                </tr>
              )
            })}
          </tbody>
        </table>
      </div>

      {plan.missing.length > 0 ? (
        <Alert variant="destructive">
          <AlertTriangle className="size-4" />
          <AlertTitle>Missing files</AlertTitle>
          <AlertDescription>
            {plan.missing.map((category) => CUB_CATEGORY_LABELS[category]).join(", ")}
          </AlertDescription>
        </Alert>
      ) : null}

      {plan.duplicates.length > 0 ? (
        <p className="text-xs text-muted-foreground">
          Ignored duplicates:{" "}
          {plan.duplicates.map((duplicate) => duplicate.name).join(", ")}
        </p>
      ) : null}

      {plan.unrecognized.length > 0 ? (
        <p className="text-xs text-muted-foreground">
          {plan.unrecognized.length} other file(s) in the folder were skipped.
        </p>
      ) : null}

      <Button type="button" variant="ghost" size="sm" onClick={onReset}>
        Clear selection
      </Button>
    </div>
  )
}

function CategoryStatusBadge({
  hasFile,
  result,
}: {
  hasFile: boolean
  result: CubFolderUploadFileResult | undefined
}) {
  if (result) {
    if (isApiSuccess(result)) {
      return (
        <Badge variant="secondary" className="gap-1">
          <CheckCircle2 className="size-3" /> Uploaded
        </Badge>
      )
    }
    return (
      <Badge variant="destructive" className="gap-1">
        <XCircle className="size-3" /> Failed
      </Badge>
    )
  }

  if (hasFile) {
    return (
      <Badge variant="outline" className="gap-1">
        <CheckCircle2 className="size-3" /> Ready
      </Badge>
    )
  }

  return (
    <Badge variant="ghost" className="gap-1 text-muted-foreground">
      <AlertTriangle className="size-3" /> Missing
    </Badge>
  )
}
