import { useRef, useEffect, memo } from 'react';
import { Badge } from '../ui/badge';
import { cn } from '../../lib/cn';
import { levelDotColor, levelBadgeVariant } from '../../lib/constants';
import { formatTimeAgo, truncate, basename, shortSession } from '../../lib/format';
import type { LogEntry } from '../../hooks/use-logs';

// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------

const LEVEL_BORDER_LEFT: Record<string, string> = {
  debug: 'border-l-outline/30',
  info: 'border-l-primary/50',
  warn: 'border-l-secondary/70',
  error: 'border-l-tertiary/80',
};

const COMPONENT_COLOR: Record<string, string> = {
  context: 'text-primary/70',
  hooks: 'text-on-surface-variant/70',
  agent: 'text-secondary/80',
  daemon: 'text-on-surface-variant/60',
  capture: 'text-primary/60',
  processor: 'text-on-surface-variant/70',
  lifecycle: 'text-primary/50',
  embedding: 'text-on-surface-variant/60',
  power: 'text-on-surface-variant/50',
  server: 'text-on-surface-variant/60',
};

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

function formatTimestamp(iso: string, relative: boolean): string {
  if (relative) return formatTimeAgo(iso);
  const d = new Date(iso);
  const hh = String(d.getHours()).padStart(2, '0');
  const mm = String(d.getMinutes()).padStart(2, '0');
  const ss = String(d.getSeconds()).padStart(2, '0');
  return `${hh}:${mm}:${ss}`;
}

// ---------------------------------------------------------------------------
// Data hint: pick the single most informative field out of a log entry's
// `data` blob to render inline next to the message. Turns "8 identical-looking
// rows" into "8 visually-distinct rows" without forcing the user to open the
// detail panel.
// ---------------------------------------------------------------------------

/** Max chars for a task subject preview — keeps the list row compact. */
const TASK_SUBJECT_HINT_MAX = 40;
/** Max chars for an error preview — long enough to identify, short enough to fit. */
const ERROR_HINT_MAX = 60;

/** Non-empty string or null — used by pick functions to skip absent fields. */
function nonEmpty(v: unknown): string | null {
  return typeof v === 'string' && v.length > 0 ? v : null;
}

/**
 * Priority-ordered rules. The first rule whose `pick` returns a non-empty
 * value wins; `format` (optional) post-processes it for display. New rules are
 * a one-liner — no branching, no reordering.
 */
const HINT_RULES: Array<{
  pick: (d: Record<string, unknown>) => string | null;
  format?: (v: string) => string;
}> = [
  { pick: (d) => nonEmpty(d.tool_name) },
  { pick: (d) => nonEmpty(d.type) },
  { pick: (d) => nonEmpty(d.agent_type) },
  { pick: (d) => nonEmpty(d.task_subject), format: (s) => truncate(s, TASK_SUBJECT_HINT_MAX) },
  { pick: (d) => nonEmpty(d.filename) },
  { pick: (d) => nonEmpty(d.source_path) ?? nonEmpty(d.path), format: basename },
  { pick: (d) => nonEmpty(d.error), format: (s) => truncate(s, ERROR_HINT_MAX) },
  { pick: (d) => (typeof d.pid === 'number' ? `pid ${d.pid}` : null) },
];

function dataHint(data: Record<string, unknown> | null): string {
  if (!data) return '';
  for (const { pick, format } of HINT_RULES) {
    const v = pick(data);
    if (v) return format ? format(v) : v;
  }
  return '';
}

// ---------------------------------------------------------------------------
// Component
// ---------------------------------------------------------------------------

interface LogTableProps {
  entries: LogEntry[];
  selectedId: number | null;
  onSelect: (entry: LogEntry) => void;
  autoScroll?: boolean;
  relativeTime?: boolean;
  compact?: boolean;
}

export function LogTable({
  entries,
  selectedId,
  onSelect,
  autoScroll = false,
  relativeTime = false,
  compact = true,
}: LogTableProps) {
  const scrollRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!autoScroll || !scrollRef.current) return;
    scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
  }, [entries, autoScroll]);

  return (
    <div
      ref={scrollRef}
      className="flex-1 overflow-y-auto bg-surface-container-lowest font-mono text-xs"
    >
      {entries.length === 0 ? (
        <div className="flex h-full min-h-[200px] items-center justify-center text-on-surface-variant/50 font-sans text-sm">
          No log entries
        </div>
      ) : (
        <table className="w-full border-collapse" aria-label="Log entries">
          <thead className="sticky top-0 z-10 bg-surface-container-lowest/95 backdrop-blur supports-[backdrop-filter]:bg-surface-container-lowest/80">
            <tr className="border-b border-outline-variant/10 text-[10px] uppercase tracking-[0.16em] text-on-surface-variant/50">
              <th className="w-[68px] px-3 py-2 text-left font-medium">Time</th>
              <th className="w-[12px] px-0 py-2" aria-hidden="true" />
              <th className="w-[48px] px-0 py-2 text-left font-medium">Level</th>
              <th className="w-[90px] px-0 py-2 text-left font-medium">Area</th>
              <th className="w-[72px] px-0 py-2 text-left font-medium">Session</th>
              <th className="px-3 py-2 text-left font-medium">Message</th>
            </tr>
          </thead>
          <tbody>
            {entries.map((entry) => (
              <LogRow
                key={entry.id}
                entry={entry}
                selected={entry.id === selectedId}
                onSelect={onSelect}
                relativeTime={relativeTime}
                compact={compact}
              />
            ))}
          </tbody>
        </table>
      )}
    </div>
  );
}

// ---------------------------------------------------------------------------
// Row
// ---------------------------------------------------------------------------

const LogRow = memo(function LogRow({
  entry,
  selected,
  onSelect,
  relativeTime,
  compact,
}: {
  entry: LogEntry;
  selected: boolean;
  onSelect: (entry: LogEntry) => void;
  relativeTime: boolean;
  compact: boolean;
}) {
  const py = compact ? 'py-0.5' : 'py-1.5';
  const session = shortSession(entry.session_id);
  const hint = dataHint(entry.data);

  return (
    <tr
      className={cn(
        'cursor-pointer transition-colors border-l-2',
        LEVEL_BORDER_LEFT[entry.level],
        selected
          ? 'bg-primary/10'
          : 'hover:bg-surface-container-high/30',
      )}
      onClick={() => onSelect(entry)}
    >
      <td className={cn('whitespace-nowrap pl-3 pr-2 text-on-surface-variant/50 align-top w-[68px] tabular-nums', py)}>
        {formatTimestamp(entry.timestamp, relativeTime)}
      </td>
      <td className={cn('whitespace-nowrap pr-1 align-top w-[12px]', py)}>
        <div className={cn('h-1.5 w-1.5 rounded-full mt-1', levelDotColor(entry.level as 'debug' | 'info' | 'warn' | 'error'))} />
      </td>
      <td className={cn('whitespace-nowrap pr-2 align-top w-[48px]', py)}>
        <Badge
          variant={levelBadgeVariant(entry.level as 'debug' | 'info' | 'warn' | 'error')}
          className="px-1 py-0 text-[9px] uppercase"
        >
          {entry.level}
        </Badge>
      </td>
      <td className={cn('whitespace-nowrap pr-2 align-top w-[90px] truncate max-w-[90px]', py, COMPONENT_COLOR[entry.component] ?? 'text-on-surface-variant/60')}>
        {entry.component}
      </td>
      <td
        className={cn('whitespace-nowrap pr-2 align-top w-[72px] tabular-nums text-on-surface-variant/40', py)}
        title={entry.session_id ?? ''}
      >
        {session}
      </td>
      <td className={cn('pr-3 text-on-surface align-top break-words', py)}>
        <span>{entry.message}</span>
        {hint && (
          <span className="ml-2 text-on-surface-variant/50">· {hint}</span>
        )}
      </td>
    </tr>
  );
});
