"use client";

import {
  flexRender,
  getCoreRowModel,
  useReactTable,
} from "@tanstack/react-table";
import { AnimatePresence, motion } from "framer-motion";
import { ChevronsDown } from "lucide-react";
import { useImperativeHandle, useRef } from "react";

import {
  loadLatestFindingTriageNote,
  updateFindingTriage,
} from "@/actions/findings";
import { Skeleton } from "@/components/shadcn/skeleton/skeleton";
import { LoadingState } from "@/components/shadcn/spinner/loading-state";
import { TableCell, TableRow } from "@/components/shadcn/table";
import { useFindingGroupResourceState } from "@/hooks/use-finding-group-resource-state";
import { useScrollHint } from "@/hooks/use-scroll-hint";
import { cn } from "@/lib/utils";
import { FindingGroupRow } from "@/types";

import { getColumnFindingResources } from "./column-finding-resources";
import { FindingsSelectionContext } from "./findings-selection-context";
import {
  getFilteredFindingGroupResourceCount,
  getFindingGroupEmptyStateMessage,
  getFindingGroupSkeletonCount,
} from "./inline-resource-container.utils";
import { ResourceDetailDrawer } from "./resource-detail-drawer";

export interface InlineResourceContainerHandle {
  /** Soft-refresh resources (re-fetch page 1 without skeletons). */
  refresh: () => void;
  /** Clear internal row selection and notify parent. */
  clearSelection: () => void;
}

interface InlineResourceContainerProps {
  group: FindingGroupRow;
  resolvedFilters: Record<string, string>;
  hasHistoricalData: boolean;
  resourceSearch: string;
  columnCount: number;
  /** Called with selected finding IDs (real UUIDs) for parent-level mute */
  onResourceSelectionChange: (findingIds: string[]) => void;
  ref?: React.Ref<InlineResourceContainerHandle>;
}

// NOTE: We intentionally do NOT auto-select child resources when a parent group
// is selected. Group-level mute resolution now fetches the group's visible
// resources separately. Auto-selecting children would still require syncing state
// with infinite scroll (resources load 10 at a time), causing cascading setState
// during render and confusing partial selections. Resource-level checkboxes are
// for selecting a specific subset independently.

/** Max skeleton rows that fit in the 440px scroll container */
const MAX_SKELETON_ROWS = 7;
const ACTIONS_COLUMN_ID = "actions";
const COMPACT_LABELED_COLUMN_IDS = new Set([
  "service",
  "region",
  "lastSeen",
  "failingFor",
  "triage",
]);
const STICKY_RESOURCE_ACTION_CELL_CLASS =
  "sticky right-0 z-20 min-w-12 last:rounded-r-none! overflow-visible bg-bg-neutral-secondary before:pointer-events-none before:absolute before:inset-y-0 before:-left-8 before:w-8 before:bg-gradient-to-r before:from-transparent before:to-bg-neutral-secondary before:content-[''] group-hover:bg-bg-neutral-tertiary group-hover:before:to-bg-neutral-tertiary group-data-[state=selected]:bg-bg-neutral-tertiary group-data-[state=selected]:before:to-bg-neutral-tertiary";

const getResourceCellClassName = (columnId: string) =>
  cn(
    COMPACT_LABELED_COLUMN_IDS.has(columnId) && "align-top",
    columnId === ACTIONS_COLUMN_ID && STICKY_RESOURCE_ACTION_CELL_CLASS,
  );

function ResourceSkeletonRow({
  isEmptyStateSized = false,
}: {
  isEmptyStateSized?: boolean;
}) {
  const cellClassName = isEmptyStateSized ? "h-24 py-3" : "py-3";

  return (
    <TableRow className="hover:bg-transparent">
      {/* Select: indicator + corner arrow + checkbox */}
      <TableCell className={cellClassName}>
        <div className="flex items-center gap-2">
          <Skeleton className="size-1.5 rounded-full" />
          <Skeleton className="size-4 rounded" />
          <div className="bg-bg-input-primary border-border-input-primary size-5 rounded-sm border shadow-[0_1px_2px_0_rgba(0,0,0,0.1)]" />
        </div>
      </TableCell>
      {/* Status */}
      <TableCell className={cellClassName}>
        <Skeleton className="h-6 w-11 rounded-md" />
      </TableCell>
      {/* Resource: name + uid */}
      <TableCell className={cellClassName}>
        <div className="space-y-1.5">
          <Skeleton className="h-4 w-32 rounded" />
          <Skeleton className="h-3.5 w-20 rounded" />
        </div>
      </TableCell>
      {/* Provider: alias + uid */}
      <TableCell className={cellClassName}>
        <div className="space-y-1.5">
          <Skeleton className="h-4 w-24 rounded" />
          <Skeleton className="h-3.5 w-16 rounded" />
        </div>
      </TableCell>
      {/* Severity */}
      <TableCell className={cellClassName}>
        <div className="flex items-center gap-2">
          <Skeleton className="size-2 rounded-full" />
          <Skeleton className="h-4.5 w-12 rounded" />
        </div>
      </TableCell>
      {/* Service */}
      <TableCell className={cellClassName}>
        <Skeleton className="h-4.5 w-16 rounded" />
      </TableCell>
      {/* Region */}
      <TableCell className={cellClassName}>
        <Skeleton className="h-4.5 w-20 rounded" />
      </TableCell>
      {/* Last seen */}
      <TableCell className={cellClassName}>
        <Skeleton className="h-4.5 w-24 rounded" />
      </TableCell>
      {/* Failing for */}
      <TableCell className={cellClassName}>
        <Skeleton className="h-4.5 w-16 rounded" />
      </TableCell>
      {/* Triage */}
      <TableCell className={cellClassName}>
        <Skeleton className="h-8 w-20 rounded-lg" />
      </TableCell>
      {/* Actions */}
      <TableCell
        className={cn(cellClassName, STICKY_RESOURCE_ACTION_CELL_CLASS)}
      >
        <div className="flex justify-end">
          <Skeleton className="size-8 rounded-md" />
        </div>
      </TableCell>
    </TableRow>
  );
}

export function InlineResourceContainer({
  group,
  resolvedFilters,
  hasHistoricalData,
  resourceSearch,
  columnCount,
  onResourceSelectionChange,
  ref,
}: InlineResourceContainerProps) {
  const scrollContainerRef = useRef<HTMLDivElement>(null);
  const filters: Record<string, string> = { ...resolvedFilters };
  if (resourceSearch) {
    filters["filter[name__icontains]"] = resourceSearch;
  }

  const skeletonRowCount = getFindingGroupSkeletonCount(
    group,
    filters,
    MAX_SKELETON_ROWS,
  );
  const filteredResourceCount = getFilteredFindingGroupResourceCount(
    group,
    filters,
  );

  const {
    rowSelection,
    resources,
    isLoading,
    sentinelRef,
    refresh,
    drawer,
    handleDrawerMuteComplete,
    selectedFindingIds,
    selectableRowCount,
    getRowCanSelect,
    clearSelection,
    isSelected,
    handleMuteComplete,
    handleRowSelectionChange,
    resolveSelectedFindingIds,
    updateTriageOptimistically,
  } = useFindingGroupResourceState({
    group,
    filters,
    hasHistoricalData,
    onResourceSelectionChange,
    scrollContainerRef,
  });

  // Scroll hint: shows "scroll for more" when content overflows
  const {
    containerRef: scrollHintContainerRef,
    sentinelRef: scrollHintSentinelRef,
    showScrollHint,
  } = useScrollHint({ refreshToken: resources.length });

  // Combine scrollContainerRef (for IntersectionObserver root) with scrollHintContainerRef
  const combinedScrollRef = (node: HTMLDivElement | null) => {
    scrollContainerRef.current = node;
    scrollHintContainerRef(node);
  };

  useImperativeHandle(ref, () => ({ refresh, clearSelection }));

  const columns = getColumnFindingResources({
    rowSelection,
    selectableRowCount,
    findingTitle: group.checkTitle,
    onTriageUpdateAction: (input) =>
      updateTriageOptimistically(input, updateFindingTriage),
    onTriageNoteLoadAction: loadLatestFindingTriageNote,
  });

  const table = useReactTable({
    data: resources,
    columns,
    enableRowSelection: getRowCanSelect,
    getCoreRowModel: getCoreRowModel(),
    onRowSelectionChange: handleRowSelectionChange,
    manualPagination: true,
    state: {
      rowSelection,
    },
  });

  const rows = table.getRowModel().rows;

  return (
    <FindingsSelectionContext.Provider
      value={{
        selectedFindingIds,
        selectedFindings: [],
        clearSelection,
        isSelected,
        resolveMuteIds: resolveSelectedFindingIds,
        onMuteComplete: handleMuteComplete,
      }}
    >
      <tr>
        <td colSpan={columnCount} className="max-w-0 p-0">
          <AnimatePresence initial>
            <motion.div
              // Onboarding anchor: the "Review the affected resources" tour step.
              data-tour-id="explore-findings-resources"
              initial={{ height: 0, opacity: 0 }}
              animate={{ height: "auto", opacity: 1 }}
              exit={{ height: 0, opacity: 0 }}
              transition={{ duration: 0.2, ease: "easeOut" }}
              className="overflow-hidden"
            >
              <div className="relative">
                <div
                  ref={combinedScrollRef}
                  className="minimal-scrollbar max-h-[440px] overflow-auto pl-6"
                >
                  {/* Resource rows or skeleton placeholder */}
                  <table className="-mt-2.5 w-max min-w-full border-separate border-spacing-y-4">
                    <tbody>
                      {isLoading && rows.length === 0 ? (
                        Array.from({ length: skeletonRowCount }).map((_, i) => (
                          <ResourceSkeletonRow
                            key={i}
                            isEmptyStateSized={filteredResourceCount === 0}
                          />
                        ))
                      ) : rows.length > 0 ? (
                        rows.map((row) => (
                          <TableRow
                            key={row.id}
                            data-state={row.getIsSelected() && "selected"}
                            className="group cursor-pointer"
                            onClick={(e) => {
                              // Don't open drawer if clicking interactive elements
                              // (links, buttons, checkboxes, dropdown items)
                              const target = e.target as HTMLElement;
                              if (
                                target.closest(
                                  "a, button, input, [role=menuitem]",
                                )
                              )
                                return;
                              drawer.openDrawer(row.index);
                            }}
                          >
                            {row.getVisibleCells().map((cell) => (
                              <TableCell
                                key={cell.id}
                                className={getResourceCellClassName(
                                  cell.column.id,
                                )}
                              >
                                {flexRender(
                                  cell.column.columnDef.cell,
                                  cell.getContext(),
                                )}
                              </TableCell>
                            ))}
                          </TableRow>
                        ))
                      ) : (
                        <TableRow className="hover:bg-transparent">
                          <TableCell
                            colSpan={columns.length}
                            className="h-24 text-center"
                          >
                            {getFindingGroupEmptyStateMessage(group, filters)}
                          </TableCell>
                        </TableRow>
                      )}
                    </tbody>
                  </table>

                  {/* Loading state for infinite scroll (subsequent pages only) */}
                  {isLoading && rows.length > 0 && (
                    <LoadingState label="Loading resources..." />
                  )}

                  {/* Sentinel for scroll hint detection */}
                  <div
                    ref={scrollHintSentinelRef}
                    aria-hidden
                    className="h-px shrink-0"
                  />

                  {/* Sentinel for infinite scroll */}
                  <div ref={sentinelRef} className="h-1" />
                </div>

                {/* Gradients rendered after scroll container so they paint on top */}
                <div className="from-bg-neutral-secondary pointer-events-none absolute top-0 right-0 left-6 z-20 h-6 bg-gradient-to-b to-transparent" />
                <div className="from-bg-neutral-secondary pointer-events-none absolute right-0 bottom-0 left-6 z-20 h-6 bg-gradient-to-t to-transparent" />

                {/* Scroll hint */}
                {showScrollHint && (
                  <div className="pointer-events-none absolute right-0 bottom-0 left-6 z-30">
                    <div className="absolute inset-x-0 bottom-2 flex justify-center">
                      <div className="bg-bg-neutral-tertiary text-text-neutral-secondary animate-bounce rounded-full px-3 py-1 text-xs shadow-md">
                        <ChevronsDown className="inline size-3.5" /> Scroll for
                        more
                      </div>
                    </div>
                  </div>
                )}
              </div>
            </motion.div>
          </AnimatePresence>
        </td>
      </tr>

      <ResourceDetailDrawer
        open={drawer.isOpen}
        onOpenChange={(open) => {
          if (!open) drawer.closeDrawer();
        }}
        isLoading={drawer.isLoading}
        isNavigating={drawer.isNavigating}
        checkMeta={drawer.checkMeta}
        currentIndex={drawer.currentIndex}
        totalResources={drawer.totalResources}
        currentResource={drawer.currentResource}
        currentFinding={drawer.currentFinding}
        otherFindings={drawer.otherFindings}
        showSyntheticResourceHint={group.resourcesTotal === 0}
        onNavigatePrev={drawer.navigatePrev}
        onNavigateNext={drawer.navigateNext}
        onMuteComplete={handleDrawerMuteComplete}
        onTriageUpdate={drawer.patchTriageUpdate}
      />
    </FindingsSelectionContext.Provider>
  );
}
