import type { LucideIcon } from 'lucide-react';
import type { ReactNode } from 'react';
import { Link } from 'react-router';
import { cn } from 'helpers/cn';

// The row every list is made of. See docs/design-language-interface.md § 2 and § 5.
//
// The rule this exists to enforce is "one focal point per row": exactly one element at row-title
// rank, everything else meta. That is not a rule you can follow by remembering it — thirty views
// proved that — so the shape encodes it. `title` is the focal point and the only thing that gets
// weight; `meta` is a single dotted line; `lead` and `trail` are for a state marker and an action,
// not for more text.
//
// Rows carry their own padding and rely on the list's `divide-y` for separation. A border per row
// double-draws at every boundary and is why some lists have hairlines twice as dark as others.

type DataRowProps = {
  /** The focal point. A string is styled for you; a node is passed through for when it needs marks. */
  title: ReactNode;
  /** One line of prose under the title. Truncated — a row is scanned, not read. */
  description?: ReactNode;
  /** Secondary facts, joined with a dot. Falsy entries are dropped, so callers can inline conditions. */
  meta?: ReactNode[];
  /** State marker or avatar, vertically aligned to the title. */
  lead?: ReactNode;
  /** Counts, actions, chevrons. Right-aligned, never wraps. */
  trail?: ReactNode;
  /** Makes the whole row a link. Rows are real links so cmd-click and the back button work. */
  to?: string;
  /** Same, for a destination outside the app. Opens in a new tab; ignored when `to` is set. */
  href?: string;
  onClick?: () => void;
  /** Marks the row as the current selection when the list is a master beside a detail pane. */
  selected?: boolean;
  className?: string;
};

export const DataRow = ({
  title,
  description,
  meta,
  lead,
  trail,
  to,
  href,
  onClick,
  selected,
  className,
}: DataRowProps) => {
  const interactive = !!to || !!href || !!onClick;
  const body = (
    <>
      {lead && <div className="mt-0.5 flex shrink-0 items-center">{lead}</div>}
      <div className="min-w-0 flex-1">
        <div className="flex items-center gap-1.5">
          {typeof title === 'string' ? <span className="truncate text-sm font-medium">{title}</span> : title}
        </div>
        {description && <p className="truncate text-sm text-muted-foreground">{description}</p>}
        {meta && <RowMeta items={meta} />}
      </div>
      {trail && <div className="flex shrink-0 items-center gap-2 whitespace-nowrap">{trail}</div>}
    </>
  );

  const classes = cn(
    'flex items-start gap-3 px-4 py-3 text-left transition-colors',
    interactive && 'hover:bg-muted/50',
    selected && 'bg-muted',
    className,
  );

  if (to) {
    return (
      <Link to={to} className={classes}>
        {body}
      </Link>
    );
  }
  if (href) {
    return (
      <a href={href} target="_blank" rel="noreferrer" className={classes}>
        {body}
      </a>
    );
  }
  if (onClick) {
    return (
      <button type="button" onClick={onClick} className={cn(classes, 'w-full')}>
        {body}
      </button>
    );
  }
  return <div className={classes}>{body}</div>;
};

/**
 * The dotted meta line. Separators are rendered between items rather than after each, so a trailing
 * dot can never appear when the last entry is conditional — the failure mode of joining with a
 * string. Numbers get `tabular-nums` here for free (§ 7).
 */
export const RowMeta = ({ items, className }: { items: ReactNode[]; className?: string }) => {
  const shown = items.filter(Boolean);
  if (!shown.length) return null;
  return (
    <div
      className={cn(
        'mt-1 flex flex-wrap items-center gap-x-1.5 gap-y-0.5 text-xs tabular-nums text-muted-foreground',
        className,
      )}
    >
      {shown.map((item, index) => (
        <span key={index} className="flex items-center gap-1.5">
          {index > 0 && (
            <span aria-hidden className="text-muted-foreground/40">
              &middot;
            </span>
          )}
          {item}
        </span>
      ))}
    </div>
  );
};

/** An icon paired with its own value inside a meta line — they are one object, hence `gap-1.5`. */
export const MetaItem = ({ icon: Icon, children }: { icon?: LucideIcon; children: ReactNode }) => (
  <span className="flex items-center gap-1.5">
    {Icon && <Icon className="h-3.5 w-3.5" />}
    {children}
  </span>
);

/** The container a list of DataRows goes in: scrolls, and draws the one hairline between rows. */
export const DataList = ({ children, className }: { children: ReactNode; className?: string }) => (
  <div className={cn('h-full overflow-y-auto', className)}>
    <div className="flex flex-col divide-y">{children}</div>
  </div>
);
