import React from 'react';
import type { ButtonHTMLAttributes, ComponentType, ReactElement, ReactNode } from 'react';
import { useHostNotifications } from '@nexuscrm/host-sdk';

export interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: 'primary' | 'secondary' | 'ghost' | 'danger' | 'success';
  size?: 'sm' | 'md' | 'lg' | 'icon';
  loading?: boolean;
  icon?: ReactNode;
  fullWidth?: boolean;
  children?: ReactNode;
}

export function Button({
  loading,
  disabled,
  children,
  icon,
  variant: _variant,
  size: _size,
  fullWidth: _fullWidth,
  ...props
}: ButtonProps): ReactElement {
  return (
    <button type={props.type ?? 'button'} disabled={disabled || loading} {...props}>
      {icon}
      {loading ? 'Loading...' : children}
    </button>
  );
}

export interface OverflowMenuItem {
  key: string;
  label: ReactNode;
  icon?: ReactNode;
  onSelect: () => void;
  disabled?: boolean;
  title?: string;
  variant?: 'default' | 'danger';
  dividerBefore?: boolean;
}

export interface OverflowMenuProps {
  items: OverflowMenuItem[];
  label?: ReactNode;
  align?: 'start' | 'end';
  disabled?: boolean;
  'aria-label'?: string;
  className?: string;
  triggerClassName?: string;
}

// Test stub: renders the trigger + all items as visible menuitems (the real
// component collapses them behind a dropdown). Lets tests assert overflow
// membership, variant, disabled, divider, and click behavior without driving
// the portal. Real interaction is covered by the OverflowMenu unit tests.
// triggerClassName is forwarded onto the trigger to mirror the real prop
// contract (callers size the trigger to the toolbar height, e.g. h-8).
export function OverflowMenu({ items, disabled, label = 'More', 'aria-label': ariaLabel = 'More actions', triggerClassName }: OverflowMenuProps): ReactElement {
  return (
    <div data-testid="overflow-menu">
      <button type="button" className={triggerClassName} disabled={disabled} aria-haspopup="menu" aria-label={ariaLabel}>{label}</button>
      <div role="menu" aria-label={ariaLabel}>
        {items.flatMap((item) => {
          const nodes: ReactElement[] = [];
          if (item.dividerBefore) nodes.push(<hr key={`${item.key}-divider`} role="separator" />);
          nodes.push(
            <button
              key={item.key}
              type="button"
              role="menuitem"
              data-variant={item.variant ?? 'default'}
              disabled={item.disabled}
              title={item.title}
              onClick={item.onSelect}
            >
              {item.label}
            </button>,
          );
          return nodes;
        })}
      </div>
    </div>
  );
}

export function Badge({ children, className }: { children?: ReactNode; className?: string }): ReactElement {
  return <span className={className}>{children}</span>;
}

export interface ConfirmationModalProps {
  isOpen: boolean;
  onClose: () => void;
  onConfirm: () => void;
  title: string;
  message?: string;
  confirmLabel?: string;
  loading?: boolean;
}

export function ConfirmationModal({
  isOpen,
  onConfirm,
  title,
  message,
  confirmLabel = 'Confirm',
  loading,
}: ConfirmationModalProps): ReactElement | null {
  if (!isOpen) {
    return null;
  }
  return (
    <section role="dialog" aria-label={title}>
      <h1>{title}</h1>
      {message && <p>{message}</p>}
      <button type="button" disabled={loading} onClick={onConfirm}>
        {confirmLabel}
      </button>
    </section>
  );
}

export function RecordListSkeleton({ message }: { columns?: number; message?: string }): ReactElement {
  return <div>{message ?? 'Loading...'}</div>;
}

export interface SkeletonProps {
  variant?: 'text' | 'rectangular' | 'circular';
  width?: number | string;
  height?: number | string;
  className?: string;
}

export function Skeleton({ variant = 'text' }: SkeletonProps): ReactElement {
  return <div data-testid="skeleton" data-variant={variant} />;
}

export interface AccessDeniedEmptyStateProps {
  title?: string;
  description?: string;
  onGoBack?: () => void;
}

export function AccessDeniedEmptyState({ title, description, onGoBack }: AccessDeniedEmptyStateProps): ReactElement {
  return (
    <section aria-label="Access denied">
      <h2>{title ?? 'Access denied'}</h2>
      {description ? <p>{description}</p> : null}
      {onGoBack ? <button type="button" onClick={onGoBack}>Go back</button> : null}
    </section>
  );
}

export interface PaginationProps {
  page: number;
  pageSize: number;
  totalRecords?: number;
  hasNext?: boolean;
  onPageChange: (page: number) => void;
  onPageSizeChange: (pageSize: number) => void;
}

export function Pagination({ page }: PaginationProps): ReactElement {
  return <nav aria-label="Pagination">Page {page}</nav>;
}

export interface BaseModalProps {
  isOpen: boolean;
  onClose: () => void;
  title: ReactNode;
  description?: ReactNode;
  icon?: ComponentType<{ className?: string }>;
  iconClassName?: string;
  iconBgClassName?: string;
  headerClassName?: string;
  maxWidth?: 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | '5xl' | 'full';
  children: ReactNode;
  footer?: ReactNode;
  closeButtonLight?: boolean;
  overlayClassName?: string;
}

export function BaseModal({
  isOpen,
  title,
  children,
  footer,
}: BaseModalProps): ReactElement | null {
  if (!isOpen) {
    return null;
  }
  return (
    <section role="dialog" aria-label={String(title)}>
      <h1>{title}</h1>
      {children}
      {footer}
    </section>
  );
}

export interface EmptyStateProps {
  title: string;
  description?: string;
  action?: { label: string; onClick: () => void; icon?: ReactNode } | ReactNode;
}

function isActionObject(a: unknown): a is { label: string; onClick: () => void } {
  return !!a && typeof a === 'object' && 'label' in (a as Record<string, unknown>) && 'onClick' in (a as Record<string, unknown>);
}

export function EmptyState({ title, description, action }: EmptyStateProps): ReactElement {
  return (
    <div>
      <h2>{title}</h2>
      {description && <p>{description}</p>}
      {isActionObject(action)
        ? <button type="button" onClick={action.onClick}>{action.label}</button>
        : (action as ReactNode)}
    </div>
  );
}

export interface SearchableSelectOption {
  value: string;
  label: string;
}

export interface SearchableSelectProps {
  options: readonly SearchableSelectOption[];
  value: string;
  onChange: (value: string) => void;
  placeholder?: string;
  disabled?: boolean;
  // Mirror the real SearchableSelect's a11y contract so tests can assert that
  // an invalid required picklist is announced/linked (id + aria-invalid +
  // aria-describedby derived from `error`).
  error?: boolean;
  id?: string;
}

export function SearchableSelect({
  options,
  value,
  onChange,
  placeholder,
  disabled,
  error = false,
  id,
}: SearchableSelectProps): ReactElement {
  return (
    <select
      id={id}
      aria-label={placeholder ?? 'Select'}
      aria-invalid={error || undefined}
      aria-describedby={error && id ? `${id}-error` : undefined}
      value={value}
      disabled={disabled}
      onChange={(event) => onChange(event.target.value)}
    >
      {placeholder && <option value="">{placeholder}</option>}
      {options.map((option) => (
        <option key={option.value} value={option.value}>
          {option.label}
        </option>
      ))}
    </select>
  );
}

export function ToastContainer(): ReactElement | null {
  return null;
}

export function useErrorToast() {
  return useHostNotifications().error;
}

export function useSuccessToast() {
  return useHostNotifications().success;
}

export function useInfoToast() {
  return useHostNotifications().info;
}

export function useWarningToast() {
  return useHostNotifications().warning;
}

export const KEYS = {
  ENTER: 'Enter',
  ESCAPE: 'Escape',
} as const;

export const DEBOUNCE_FAST_MS = 0;
export const DEFAULT_PAGE_SIZE = 50;

// ── PowerList (test stub) ────────────────────────────────────────────────
// Faithful-enough stub of the platform PowerList used by the identity-sync
// tabs (Activity / Quarantine): renders loading, the empty state, and data
// rows via each column's `render`, plus a per-row actions cell — the surfaces
// the source-tests assert against.
export interface PowerListColumn<T> {
  key: string;
  label: string;
  sortable?: boolean;
  width?: string;
  minWidth?: string;
  render?: (value: unknown, row: T, index: number) => ReactNode;
  align?: 'left' | 'center' | 'right';
  searchable?: boolean;
  hidden?: boolean;
}

export interface PowerListProps<T> {
  data: T[];
  columns: PowerListColumn<T>[];
  loading?: boolean;
  rowKey: (row: T) => string;
  onRowClick?: (row: T) => void;
  searchable?: boolean;
  searchPlaceholder?: string;
  emptyIcon?: ReactNode;
  emptyTitle?: string;
  emptyMessage?: string;
  emptyAction?: { label: string; onClick: () => void };
  actions?: (row: T) => ReactNode;
  actionsLabel?: string;
  leadingColumn?: { width?: string; render: (row: T) => ReactNode };
  defaultSort?: { key: string; direction: 'asc' | 'desc' };
  ariaLabel?: string;
}

export function PowerList<T>({
  data,
  columns,
  loading,
  rowKey,
  actions,
  actionsLabel,
  emptyIcon,
  emptyTitle,
  emptyMessage,
  emptyAction,
  ariaLabel,
}: PowerListProps<T>): ReactElement {
  if (loading) {
    return <div role="status">Loading…</div>;
  }
  if (!data || data.length === 0) {
    return (
      <div>
        {emptyIcon}
        {emptyTitle ? <h3>{emptyTitle}</h3> : null}
        {emptyMessage ? <p>{emptyMessage}</p> : null}
        {emptyAction ? <button onClick={emptyAction.onClick}>{emptyAction.label}</button> : null}
      </div>
    );
  }
  const visible = columns.filter((c) => !c.hidden);
  return (
    <table aria-label={ariaLabel}>
      <thead>
        <tr>
          {visible.map((c) => (
            <th key={c.key}>{c.label}</th>
          ))}
          {actions ? <th>{actionsLabel ?? 'Actions'}</th> : null}
        </tr>
      </thead>
      <tbody>
        {data.map((row, index) => (
          <tr key={rowKey(row)}>
            {visible.map((c) => (
              <td key={c.key}>
                {c.render
                  ? c.render((row as Record<string, unknown>)[c.key], row, index)
                  : String((row as Record<string, unknown>)[c.key] ?? '')}
              </td>
            ))}
            {actions ? <td>{actions(row)}</td> : null}
          </tr>
        ))}
      </tbody>
    </table>
  );
}

// ── Tooltip (test stub) ──────────────────────────────────────────────────
// Renders the wrapped children (the interactive content the tests drive) and
// exposes string tooltip content via `title` so it stays queryable without
// injecting stray text nodes.
export function Tooltip({
  content,
  children,
}: {
  content?: ReactNode;
  children?: ReactNode;
  position?: 'top' | 'bottom' | 'left' | 'right';
}): ReactElement {
  return <span title={typeof content === 'string' ? content : undefined}>{children}</span>;
}
