"use client";

import { AlertTriangle, ChevronDown, ChevronRight, ExternalLink } from "lucide-react";
import * as React from "react";

import { Badge } from "@/components/ui/badge";
import { Checkbox } from "@/components/ui/checkbox";
import {
  Table,
  TableBody,
  TableCell,
  TableHead,
  TableHeader,
  TableRow,
} from "@/components/ui/table";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { cn } from "@/lib/utils";

import {
  groupPreviewRowsByStructure,
  groupSelectionState,
  type PreviewRowGroup,
} from "../_lib/group-preview-rows";
import { SCOPE_LABELS, type PreviewRow } from "../_lib/schema";

type Props = {
  rows: PreviewRow[];
  selectedIds: Set<number>;
  /**
   * Ticks or unticks any number of rows in one state update — a single
   * record, a master-ISIN group or the whole list.
   */
  onToggleIds: (ids: number[], selected: boolean) => void;
  readOnly?: boolean;
};

const COLUMN_COUNT = 11;

const NUMBER_FORMAT = new Intl.NumberFormat("en-GB", {
  minimumFractionDigits: 2,
  maximumFractionDigits: 6,
});

function formatValue(value: number | null): string {
  return value === null ? "—" : NUMBER_FORMAT.format(value);
}

/**
 * Underlying legs store no amount column, so their value is quantity x price.
 * That figure is shown in muted type to keep it visibly distinct from a stored
 * amount — it is never written back to the row.
 */
function AmountCell({
  amount,
  derived,
}: {
  amount: number | null;
  derived: number | null;
}) {
  if (amount !== null) {
    return <>{formatValue(amount)}</>;
  }

  if (derived === null) {
    return <>—</>;
  }

  return (
    <Tooltip>
      <TooltipTrigger asChild>
        <span className="text-muted-foreground underline decoration-dotted">
          {formatValue(derived)}
        </span>
      </TooltipTrigger>
      <TooltipContent>
        Quantity x purchase price. This leg has no stored amount, so nothing is
        written to that column.
      </TooltipContent>
    </Tooltip>
  );
}

/**
 * The Bulk Edit output list: one row per affected record, Old beside New.
 *
 * Rows are grouped under their master (structure) ISIN: the structure-master
 * record first, then every temp and TRX transaction that belongs to it.
 * Ticking the master ISIN — either the group header or the structure-master
 * record itself — ticks all of its transactions, so an operator can take a
 * whole structure in or out of the split in one click while still being able
 * to untick a single record underneath.
 */
export function PreviewTable({
  rows,
  selectedIds,
  onToggleIds,
  readOnly = false,
}: Props) {
  const groups = React.useMemo(() => groupPreviewRowsByStructure(rows), [rows]);

  // Groups start collapsed: an action can touch dozens of structures, and the
  // master-ISIN line already carries the counts and the cascade checkbox, so
  // the operator only opens the ones they want to inspect record by record.
  // Expanded keys are tracked so the set survives a re-preview that rebuilds
  // the rows but keeps the same structures.
  const [expanded, setExpanded] = React.useState<Set<string>>(() => new Set());
  const toggleExpanded = (key: string) => {
    setExpanded((current) => {
      const next = new Set(current);
      if (next.has(key)) next.delete(key);
      else next.add(key);
      return next;
    });
  };

  // With every group folded the table is a list of master ISINs, so the
  // per-record column headings (date, Old, New) would label nothing.
  const anyExpanded = groups.some((group) => expanded.has(group.key));

  const selectableIds = React.useMemo(
    () => rows.filter((row) => !row.applied).map((row) => row.id),
    [rows],
  );
  const allSelected =
    selectableIds.length > 0 && selectableIds.every((id) => selectedIds.has(id));
  const someSelected = selectableIds.some((id) => selectedIds.has(id));

  if (rows.length === 0) {
    return (
      <div className="rounded-lg border bg-card p-8 text-center text-muted-foreground text-sm">
        No records match this underlying and date range.
      </div>
    );
  }

  return (
    <div className="overflow-x-auto rounded-lg border bg-card">
      <Table>
        <TableHeader className="bg-muted/15">
          <TableRow>
            <TableHead rowSpan={anyExpanded ? 2 : 1} className="w-10 px-3 align-bottom">
              {!readOnly ? (
                <Checkbox
                  aria-label="Select all rows"
                  checked={allSelected ? true : someSelected ? "indeterminate" : false}
                  onCheckedChange={(checked) => onToggleIds(selectableIds, checked === true)}
                />
              ) : null}
            </TableHead>
            <TableHead
              rowSpan={anyExpanded ? 2 : 1}
              colSpan={anyExpanded ? 1 : COLUMN_COUNT - 1}
              className="px-3 align-bottom whitespace-nowrap"
            >
              {anyExpanded ? "Master ISIN / Transaction" : "Master ISIN"}
            </TableHead>
            {anyExpanded ? (
              <>
                <TableHead rowSpan={2} className="px-3 align-bottom whitespace-nowrap">
                  Date
                </TableHead>
                <TableHead
                  colSpan={4}
                  className="border-l px-3 text-center font-medium whitespace-nowrap"
                >
                  Old
                </TableHead>
                <TableHead
                  colSpan={4}
                  className="border-l px-3 text-center font-medium whitespace-nowrap"
                >
                  New
                </TableHead>
              </>
            ) : null}
          </TableRow>
          {anyExpanded ? (
            <TableRow>
              <TableHead className="border-l px-3 text-right text-xs">Quantity</TableHead>
              <TableHead className="px-3 text-right text-xs">Purchase Price</TableHead>
              <TableHead className="px-3 text-right text-xs">Initial Level</TableHead>
              <TableHead className="px-3 text-right text-xs">Amount</TableHead>
              <TableHead className="border-l px-3 text-right text-xs">Quantity</TableHead>
              <TableHead className="px-3 text-right text-xs">Purchase Price</TableHead>
              <TableHead className="px-3 text-right text-xs">Initial Level</TableHead>
              <TableHead className="px-3 text-right text-xs">Amount</TableHead>
            </TableRow>
          ) : null}
        </TableHeader>
        <TableBody>
          {groups.map((group) => {
            const isCollapsed = !expanded.has(group.key);

            return (
              <React.Fragment key={group.key}>
                <GroupHeaderRow
                  group={group}
                  selectedIds={selectedIds}
                  collapsed={isCollapsed}
                  onToggleCollapsed={() => toggleExpanded(group.key)}
                  onToggleGroup={(selected) => onToggleIds(group.selectableIds, selected)}
                  readOnly={readOnly}
                />
                {isCollapsed
                  ? null
                  : [...group.masterRows, ...group.transactionRows].map((row) => (
                      <PreviewTableRow
                        key={row.id}
                        row={row}
                        selected={selectedIds.has(row.id)}
                        // The structure-master record governs every transaction
                        // listed under it, so ticking it selects the whole group
                        // — mirroring the group header — while a single
                        // transaction still toggles on its own.
                        onToggle={(selected) =>
                          onToggleIds(
                            row.scope === "master" ? group.selectableIds : [row.id],
                            selected,
                          )
                        }
                        readOnly={readOnly}
                      />
                    ))}
              </React.Fragment>
            );
          })}
        </TableBody>
      </Table>
    </div>
  );
}

/**
 * One line per master ISIN. Its checkbox is the "select the master ISIN"
 * control: it reflects every selectable row underneath (checked, indeterminate
 * or clear) and ticking it cascades to all of them.
 */
function GroupHeaderRow({
  group,
  selectedIds,
  collapsed,
  onToggleCollapsed,
  onToggleGroup,
  readOnly,
}: {
  group: PreviewRowGroup;
  selectedIds: Set<number>;
  collapsed: boolean;
  onToggleCollapsed: () => void;
  onToggleGroup: (selected: boolean) => void;
  readOnly: boolean;
}) {
  const { selected: selectedCount, total: selectableCount, state } = groupSelectionState(
    group,
    selectedIds,
  );
  const label = group.structureIsin ?? "No linked structure";
  const Chevron = collapsed ? ChevronRight : ChevronDown;
  const tempCount = group.transactionRows.filter((row) => row.scope === "temp").length;
  const trxCount = group.transactionRows.length - tempCount;

  return (
    <TableRow className="bg-muted/30 hover:bg-muted/40">
      <TableCell className="px-3 py-2">
        {!readOnly && selectableCount > 0 ? (
          <Checkbox
            aria-label={`Select all transactions under ${label}`}
            checked={state === "all" ? true : state === "some" ? "indeterminate" : false}
            onCheckedChange={(checked) => onToggleGroup(checked === true)}
          />
        ) : null}
      </TableCell>
      <TableCell colSpan={COLUMN_COUNT - 1} className="px-3 py-2">
        <button
          type="button"
          onClick={onToggleCollapsed}
          aria-expanded={!collapsed}
          className="flex w-full items-center gap-2 text-left"
        >
          <Chevron className="size-4 shrink-0 text-muted-foreground" />
          <span className="font-semibold text-sm">{label}</span>
          {group.masterRows.length > 0 ? (
            <Badge variant="secondary">{SCOPE_LABELS.master}</Badge>
          ) : null}
          <Badge variant="secondary">
            {group.transactionRows.length} transaction
            {group.transactionRows.length === 1 ? "" : "s"}
          </Badge>
          <span className="text-muted-foreground text-xs">
            {tempCount > 0 ? `${tempCount} temp` : null}
            {tempCount > 0 && trxCount > 0 ? " · " : null}
            {trxCount > 0 ? `${trxCount} TRX` : null}
          </span>
          {!readOnly && selectableCount > 0 ? (
            <span className="ml-auto text-muted-foreground text-xs tabular-nums">
              {selectedCount} / {selectableCount} selected
            </span>
          ) : null}
        </button>
      </TableCell>
    </TableRow>
  );
}

function PreviewTableRow({
  row,
  selected,
  onToggle,
  readOnly,
}: {
  row: PreviewRow;
  selected: boolean;
  onToggle: (selected: boolean) => void;
  readOnly: boolean;
}) {
  const isMaster = row.scope === "master";

  return (
    <TableRow className={cn("hover:bg-muted/30", row.applied && "opacity-60")}>
      <TableCell className="px-3">
        {!readOnly && !row.applied ? (
          <Checkbox
            aria-label={`Select record ${row.recordId}`}
            checked={selected}
            onCheckedChange={(checked) => onToggle(checked === true)}
          />
        ) : null}
      </TableCell>
      <TableCell className={cn("px-3 text-sm", !isMaster && "pl-9")}>
        <div className="flex flex-col gap-0.5">
          <span className="flex items-center gap-2">
            <span className={cn("font-medium", isMaster && "text-muted-foreground")}>
              {isMaster ? SCOPE_LABELS.master : (row.structureIsin ?? row.tableName)}
            </span>
            {!isMaster ? (
              <Badge variant="outline" className="h-5 px-1.5 text-[11px] font-normal">
                {SCOPE_LABELS[row.scope]}
              </Badge>
            ) : null}
          </span>

          {row.customerName ? (
            <span className="text-xs">
              {row.customerSubdomain && row.structureIsin ? (
                <a
                  href={`/customer/${row.customerSubdomain}/structure?parentIsin=${encodeURIComponent(row.structureIsin)}`}
                  target="_blank"
                  rel="noreferrer"
                  className="inline-flex items-center gap-1 font-medium text-primary hover:underline"
                >
                  {row.customerName}
                  <ExternalLink className="size-3" />
                </a>
              ) : (
                <span className="font-medium">{row.customerName}</span>
              )}
            </span>
          ) : null}

          <span className="text-muted-foreground text-xs">
            {row.tableName} #{row.recordId}
            {row.customerId ? ` · customer ${row.customerId}` : ""}
            {row.fType ? ` · ${row.fType}` : ""}
            {row.tOt ? ` · ${row.tOt}` : ""}
          </span>
          {row.schemaName ? (
            <span className="font-mono text-[11px] text-muted-foreground">
              {row.schemaName}
            </span>
          ) : null}
          {row.applied ? (
            <Badge variant="secondary" className="mt-1 w-fit">
              Applied
            </Badge>
          ) : null}
          {row.previouslyAppliedActionId ? (
            <Tooltip>
              <TooltipTrigger asChild>
                <Badge
                  variant="secondary"
                  className="mt-1 w-fit bg-amber-500/15 text-amber-700 dark:text-amber-400"
                >
                  Already split by #{row.previouslyAppliedActionId}
                </Badge>
              </TooltipTrigger>
              <TooltipContent>
                Corporate action #{row.previouslyAppliedActionId} already adjusted this
                record, so the Old values above are post-split. Ticking this row would
                split it a second time.
              </TooltipContent>
            </Tooltip>
          ) : null}
          {row.error ? (
            <span className="mt-1 text-destructive text-xs">{row.error}</span>
          ) : null}
        </div>
      </TableCell>
      <TableCell className="px-3 text-sm whitespace-nowrap">{row.tDate ?? "—"}</TableCell>

      <TableCell className="border-l px-3 text-right text-sm tabular-nums">
        {formatValue(row.old.quantity)}
      </TableCell>
      <TableCell className="px-3 text-right text-sm tabular-nums">
        {formatValue(row.old.price)}
      </TableCell>
      <TableCell className="px-3 text-right text-sm tabular-nums">
        {formatValue(row.old.tPrice)}
      </TableCell>
      <TableCell className="px-3 text-right text-sm tabular-nums">
        <AmountCell amount={row.old.amount} derived={row.old.derivedValue} />
      </TableCell>

      <TableCell className="border-l px-3 text-right font-medium text-sm tabular-nums">
        {formatValue(row.new.quantity)}
      </TableCell>
      <TableCell className="px-3 text-right font-medium text-sm tabular-nums">
        {formatValue(row.new.price)}
      </TableCell>
      <TableCell className="px-3 text-right font-medium text-sm tabular-nums">
        {formatValue(row.new.tPrice)}
      </TableCell>
      <TableCell className="px-3 text-right font-medium text-sm tabular-nums">
        <span className="inline-flex items-center gap-1.5">
          <AmountCell amount={row.new.amount} derived={row.new.derivedValue} />
          {row.hasAmountDrift ? (
            <Tooltip>
              <TooltipTrigger asChild>
                <AlertTriangle className="size-3.5 shrink-0 text-amber-600 dark:text-amber-500" />
              </TooltipTrigger>
              <TooltipContent>
                Amount changes by {formatValue(row.amountDrift)}. A split should leave
                it unchanged — check this row before applying.
              </TooltipContent>
            </Tooltip>
          ) : null}
        </span>
      </TableCell>
    </TableRow>
  );
}
