"use client"

import * as React from "react"
import {
  getCoreRowModel,
  useReactTable,
  type PaginationState,
} from "@tanstack/react-table"
import {
  ChevronDown,
  ChevronRight,
  ChevronsDownUp,
  ChevronsUpDown,
  Folder,
  FolderOpen,
  Layers,
  Pencil,
  Search,
} from "lucide-react"
import Link from "next/link"
import { useRouter } from "next/navigation"

import {
  DataTablePagination,
  useDebouncedValue,
} from "@/app/dashboard/_components/data-table"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import {
  Tooltip,
  TooltipContent,
  TooltipTrigger,
} from "@/components/ui/tooltip"
import { SETTINGS_INPUT_CLASS } from "@/components/settings/constants"
import { cn } from "@/lib/utils"

import {
  buildAssetFieldDefinitionHref,
  DEFAULT_PAGE_SIZE,
  LIST_HREF,
} from "./constants"
import { TREE_LIST_CLASS, TreeItem } from "@/app/dashboard/master-table/_components/tree"
import type { AssetFieldDefinitionRow } from "./schema"
import { formatPlainAmount } from "@/lib/format/numbers"

const PREVIEW_LIMIT = 4

const ANY_TYPE_KEY = ""

function groupByFieldType(rows: AssetFieldDefinitionRow[]) {
  const byType = new Map<string, { label: string; rows: AssetFieldDefinitionRow[] }>()
  for (const row of rows) {
    const key = row.fType
    const bucket = byType.get(key)
    if (bucket) bucket.rows.push(row)
    else
      byType.set(key, {
        label: key === ANY_TYPE_KEY ? "All types" : row.fTypeLabel !== "—" ? row.fTypeLabel : key,
        rows: [row],
      })
  }
  // "All types" first, then typed branches A–Z.
  return Array.from(byType.entries())
    .sort(([a, x], [b, y]) =>
      a === ANY_TYPE_KEY ? -1 : b === ANY_TYPE_KEY ? 1 : x.label.localeCompare(y.label),
    )
    .map(([key, value]) => ({ key, ...value }))
}


type AssetFieldDefinitionTableProps = {
  data: AssetFieldDefinitionRow[]
  filters: {
    apiField: string
    oxyField: string
  }
  totalCount: number
  page: number
  pageSize: number
  pageCount: number
}

const filterInputClass = cn(SETTINGS_INPUT_CLASS, "h-9 pl-8")

export function AssetFieldDefinitionTable({
  data,
  filters,
  totalCount,
  page,
  pageSize,
  pageCount,
}: AssetFieldDefinitionTableProps) {
  const router = useRouter()
  const [apiFieldDraft, setApiFieldDraft] = React.useState(filters.apiField)
  const [oxyFieldDraft, setOxyFieldDraft] = React.useState(filters.oxyField)
  const debouncedApiField = useDebouncedValue(apiFieldDraft)
  const debouncedOxyField = useDebouncedValue(oxyFieldDraft)

  const navigate = React.useCallback(
    (
      next: {
        apiField?: string
        oxyField?: string
        page?: number
        pageSize?: number
      },
      options?: { replace?: boolean },
    ) => {
      const href = buildAssetFieldDefinitionHref({
        apiField: next.apiField ?? filters.apiField,
        oxyField: next.oxyField ?? filters.oxyField,
        page: next.page ?? page,
        pageSize: next.pageSize ?? pageSize,
      })

      if (options?.replace) {
        router.replace(href, { scroll: false })
        return
      }

      router.push(href, { scroll: false })
    },
    [
      filters.apiField,
      filters.oxyField,
      page,
      pageSize,
      router,
    ],
  )

  React.useEffect(() => {
    setApiFieldDraft(filters.apiField)
  }, [filters.apiField])

  React.useEffect(() => {
    setOxyFieldDraft(filters.oxyField)
  }, [filters.oxyField])

  React.useEffect(() => {
    const normalized = debouncedApiField.trim()
    if (normalized === filters.apiField) return
    navigate({ apiField: normalized, page: 1 }, { replace: true })
  }, [debouncedApiField, filters.apiField, navigate])

  React.useEffect(() => {
    const normalized = debouncedOxyField.trim()
    if (normalized === filters.oxyField) return
    navigate({ oxyField: normalized, page: 1 }, { replace: true })
  }, [debouncedOxyField, filters.oxyField, navigate])

  // Pagination state only; the grouped list below renders the rows itself.
  const pagination = React.useMemo<PaginationState>(
    () => ({
      pageIndex: Math.max(page - 1, 0),
      pageSize,
    }),
    [page, pageSize],
  )

  const table = useReactTable({
    data,
    columns: [],
    state: { pagination },
    pageCount,
    manualPagination: true,
    onPaginationChange: (updater) => {
      const next = typeof updater === "function" ? updater(pagination) : updater
      const nextPageSize = next.pageSize > 0 ? next.pageSize : pageSize
      const nextPage = nextPageSize !== pageSize ? 1 : Math.max(next.pageIndex + 1, 1)

      navigate({
        page: nextPage,
        pageSize: nextPageSize,
      })
    },
    getCoreRowModel: getCoreRowModel(),
    getRowId: (r) => r.id,
  })

  // The backend pages by oxy field and sends every mapping of each group, ordered by oxy field.
  const groups = (() => {
    const byKey = new Map<string, AssetFieldDefinitionRow[]>()
    for (const row of data) {
      const key = row.oxyField.toLowerCase()
      const bucket = byKey.get(key)
      if (bucket) bucket.push(row)
      else byKey.set(key, [row])
    }
    return Array.from(byKey.entries()).map(([key, rows]) => ({
      key,
      label: rows[0].oxyField,
      rows,
    }))
  })()

  // Groups start collapsed; keys are lower-cased oxy fields.
  const [expanded, setExpanded] = React.useState<Set<string>>(() => new Set())
  const toggleGroup = (key: string) =>
    setExpanded((prev) => {
      const next = new Set(prev)
      if (next.has(key)) next.delete(key)
      else next.add(key)
      return next
    })
  const allExpanded = groups.length > 0 && groups.every((g) => expanded.has(g.key))

  const start = totalCount === 0 ? 0 : (page - 1) * pageSize + 1
  const end = Math.min(page * pageSize, totalCount)

  const renderEditButton = (row: AssetFieldDefinitionRow) => (
    <Tooltip>
      <TooltipTrigger asChild>
        <Button variant="secondary" size="icon-sm" className="size-7 shrink-0" asChild>
          <Link
            href={`${LIST_HREF}/${encodeURIComponent(row.id)}`}
            aria-label={`Update ${row.apiField}`}
          >
            <Pencil className="size-3.5" />
          </Link>
        </Button>
      </TooltipTrigger>
      <TooltipContent>Update</TooltipContent>
    </Tooltip>
  )

  return (
    <div className="space-y-4">
      <div className="flex flex-col gap-3 lg:flex-row lg:items-center lg:justify-between">
        <div className="grid flex-1 gap-3 sm:grid-cols-2 lg:max-w-2xl">
          <div className="relative">
            <Search className="pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground" />
            <Input
              className={filterInputClass}
              placeholder="Search oxy field"
              aria-label="Filter by oxy field"
              value={oxyFieldDraft}
              onChange={(e) => setOxyFieldDraft(e.target.value)}
            />
          </div>
          <div className="relative">
            <Search className="pointer-events-none absolute top-1/2 left-2.5 size-4 -translate-y-1/2 text-muted-foreground" />
            <Input
              className={filterInputClass}
              placeholder="Search API field"
              aria-label="Filter by API field"
              value={apiFieldDraft}
              onChange={(e) => setApiFieldDraft(e.target.value)}
            />
          </div>
        </div>

        <div className="flex items-center justify-between gap-3 lg:justify-end">
          <p className="text-sm text-muted-foreground">
            {start}–{end} of {formatPlainAmount(totalCount)} oxy fields
          </p>
          <Button
            variant="outline"
            size="sm"
            disabled={!groups.length}
            onClick={() =>
              setExpanded(allExpanded ? new Set() : new Set(groups.map((g) => g.key)))
            }
          >
            {allExpanded ? (
              <ChevronsDownUp className="size-4" />
            ) : (
              <ChevronsUpDown className="size-4" />
            )}
            {allExpanded ? "Collapse all" : "Expand all"}
          </Button>
        </div>
      </div>

      {groups.length ? (
        <ul className="divide-y overflow-hidden rounded-lg border bg-card">
          {groups.map((group) => {
            const isOpen = expanded.has(group.key)
            const count = group.rows.length
            const panelId = `afd-${group.key.replace(/[^a-z0-9_-]/g, "_")}`
            const preview = group.rows.slice(0, PREVIEW_LIMIT)
            const hidden = count - preview.length

            return (
              <li key={group.key}>
                <button
                  type="button"
                  aria-expanded={isOpen}
                  aria-controls={panelId}
                  onClick={() => toggleGroup(group.key)}
                  className={cn(
                    "flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-muted/40 focus-visible:bg-muted/40 focus-visible:outline-none",
                    isOpen && "bg-muted/30",
                  )}
                >
                  {isOpen ? (
                    <ChevronDown className="size-4 shrink-0 text-muted-foreground" />
                  ) : (
                    <ChevronRight className="size-4 shrink-0 text-muted-foreground" />
                  )}
                  {isOpen ? (
                    <FolderOpen className="size-4 shrink-0 text-primary" />
                  ) : (
                    <Folder className="size-4 shrink-0 text-muted-foreground" />
                  )}
                  <span className="min-w-0 shrink-0 font-mono text-sm font-medium">
                    {group.label}
                  </span>
                  <Badge variant="secondary" className="shrink-0 font-normal">
                    {count} {count === 1 ? "mapping" : "mappings"}
                  </Badge>
                  {!isOpen && (
                    <span className="ml-2 hidden min-w-0 flex-1 items-center gap-1.5 overflow-hidden md:flex">
                      {preview.map((row) => (
                        <code
                          key={row.id}
                          className="max-w-48 truncate rounded bg-muted px-1.5 py-0.5 text-xs text-muted-foreground"
                        >
                          {row.apiField}
                        </code>
                      ))}
                      {hidden > 0 && (
                        <span className="shrink-0 text-xs text-muted-foreground">
                          +{hidden} more
                        </span>
                      )}
                    </span>
                  )}
                </button>

                {isOpen && (
                  <ul id={panelId} className={TREE_LIST_CLASS}>
                    {(() => {
                      // Only add the field-type level when this oxy field has typed mappings.
                      const hasTypes = group.rows.some((row) => row.fType !== ANY_TYPE_KEY)
                      const branches = hasTypes
                        ? groupByFieldType(group.rows)
                        : [{ key: ANY_TYPE_KEY, label: "", rows: group.rows }]

                      const leaf = (row: AssetFieldDefinitionRow, isLast: boolean) => (
                        <TreeItem key={row.id} isLast={isLast}>
                          <div className="flex min-w-0 items-center gap-2">
                            <code className="text-xs break-all">{row.apiField}</code>
                            {row.module !== "—" && (
                              <Badge variant="outline" className="shrink-0 font-normal">
                                {row.module}
                              </Badge>
                            )}
                          </div>
                          {renderEditButton(row)}
                        </TreeItem>
                      )

                      if (!hasTypes) {
                        return group.rows.map((row, i) => leaf(row, i === group.rows.length - 1))
                      }

                      return branches.map((branch, bi) => (
                        <TreeItem
                          key={branch.key || "any"}
                          isLast={bi === branches.length - 1}
                          nested={
                            <ul className="ml-2">
                              {branch.rows.map((row, i) => leaf(row, i === branch.rows.length - 1))}
                            </ul>
                          }
                        >
                          <div className="flex items-center gap-2 text-sm">
                            <Layers className="size-3.5 text-muted-foreground" />
                            <span className="font-medium">{branch.label}</span>
                            <Badge variant="secondary" className="font-normal">
                              {branch.rows.length}
                            </Badge>
                          </div>
                        </TreeItem>
                      ))
                    })()}
                  </ul>
                )}
              </li>
            )
          })}
        </ul>
      ) : (
        <div className="flex h-24 items-center justify-center rounded-lg border bg-card text-sm text-muted-foreground">
          No results found.
        </div>
      )}

      <DataTablePagination
        table={table}
        totalRows={totalCount}
        itemNoun="oxy field"
        idPrefix="asset-field-definition"
        pageSizes={[10, 20, DEFAULT_PAGE_SIZE, 100]}
      />
    </div>
  )
}
