/**
 * ActivityFeed Component
 *
 * A shared real-time activity feed component used across:
 * - Main Dashboard
 * - Project Dashboard
 * - Activity Feed page
 *
 * Features:
 * - Real-time connection indicator
 * - Time-based event grouping (Today, Yesterday, This Week, etc.)
 * - Event type filtering
 * - Expandable event details
 * - Approval request handling
 * - Search functionality
 */

'use client';

import { useState, useMemo, useCallback } from 'react';
import { formatDistanceToNow, isToday, isYesterday, isThisWeek } from 'date-fns';
import {
  Activity,
  Bot,
  FileText,
  Users,
  CheckCircle2,
  XCircle,
  PlayCircle,
  AlertTriangle,
  Folder,
  Workflow,
  ChevronDown,
  ChevronRight,
  Search,
  Filter,
  RefreshCw,
  CircleDot,
  GitPullRequest,
  ExternalLink,
} from 'lucide-react';
import { cn } from '@/lib/utils';
import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Checkbox } from '@/components/ui/checkbox';
import { Separator } from '@/components/ui/separator';
import { Skeleton } from '@/components/ui/skeleton';
import {
  EventType,
  type ProjectEvent,
  isAgentEvent,
  isIssueEvent,
  isSprintEvent,
  isApprovalEvent,
  isWorkflowEvent,
  isProjectEvent,
} from '@/lib/types/events';
import type { ConnectionState } from '@/lib/types/events';

// ============================================================================
// Types
// ============================================================================

export interface ActivityFeedProps {
  /** Events to display */
  events: ProjectEvent[];
  /** SSE connection state */
  connectionState: ConnectionState;
  /** Whether data is loading */
  isLoading?: boolean;
  /** Callback to trigger reconnection */
  onReconnect?: () => void;
  /** Callback when an approval is approved */
  onApprove?: (event: ProjectEvent) => void;
  /** Callback when an approval is rejected */
  onReject?: (event: ProjectEvent) => void;
  /** Callback when event is clicked */
  onEventClick?: (event: ProjectEvent) => void;
  /** Maximum height for scrolling (default: 'auto') */
  maxHeight?: number | string;
  /** Whether to show the header (default: true) */
  showHeader?: boolean;
  /** Title for the header (default: 'Activity Feed') */
  title?: string;
  /** Whether to enable filtering (default: true) */
  enableFiltering?: boolean;
  /** Whether to enable search (default: true) */
  enableSearch?: boolean;
  /** Show compact view (default: false) */
  compact?: boolean;
  /** Additional CSS classes */
  className?: string;
}

export interface EventTypeFilter {
  type: EventType | 'all';
  label: string;
  count: number;
}

interface EventGroup {
  label: string;
  events: ProjectEvent[];
}

// ============================================================================
// Event Configuration
// ============================================================================

const EVENT_TYPE_CONFIG: Record<
  string,
  {
    label: string;
    icon: typeof Activity;
    color: string;
    bgColor: string;
  }
> = {
  // Agent Events
  [EventType.AGENT_SPAWNED]: {
    label: 'Agent Spawned',
    icon: Bot,
    color: 'text-blue-500',
    bgColor: 'bg-blue-100 dark:bg-blue-900',
  },
  [EventType.AGENT_MESSAGE]: {
    label: 'Agent Message',
    icon: Bot,
    color: 'text-blue-500',
    bgColor: 'bg-blue-100 dark:bg-blue-900',
  },
  [EventType.AGENT_STATUS_CHANGED]: {
    label: 'Status Changed',
    icon: Bot,
    color: 'text-yellow-500',
    bgColor: 'bg-yellow-100 dark:bg-yellow-900',
  },
  [EventType.AGENT_TERMINATED]: {
    label: 'Agent Terminated',
    icon: Bot,
    color: 'text-gray-500',
    bgColor: 'bg-gray-100 dark:bg-gray-800',
  },
  // Issue Events
  [EventType.ISSUE_CREATED]: {
    label: 'Issue Created',
    icon: CircleDot,
    color: 'text-green-500',
    bgColor: 'bg-green-100 dark:bg-green-900',
  },
  [EventType.ISSUE_UPDATED]: {
    label: 'Issue Updated',
    icon: FileText,
    color: 'text-blue-500',
    bgColor: 'bg-blue-100 dark:bg-blue-900',
  },
  [EventType.ISSUE_ASSIGNED]: {
    label: 'Issue Assigned',
    icon: Users,
    color: 'text-purple-500',
    bgColor: 'bg-purple-100 dark:bg-purple-900',
  },
  [EventType.ISSUE_CLOSED]: {
    label: 'Issue Closed',
    icon: CheckCircle2,
    color: 'text-green-600',
    bgColor: 'bg-green-100 dark:bg-green-900',
  },
  // Sprint Events
  [EventType.SPRINT_STARTED]: {
    label: 'Sprint Started',
    icon: PlayCircle,
    color: 'text-indigo-500',
    bgColor: 'bg-indigo-100 dark:bg-indigo-900',
  },
  [EventType.SPRINT_COMPLETED]: {
    label: 'Sprint Completed',
    icon: CheckCircle2,
    color: 'text-indigo-500',
    bgColor: 'bg-indigo-100 dark:bg-indigo-900',
  },
  // Approval Events
  [EventType.APPROVAL_REQUESTED]: {
    label: 'Approval Requested',
    icon: AlertTriangle,
    color: 'text-orange-500',
    bgColor: 'bg-orange-100 dark:bg-orange-900',
  },
  [EventType.APPROVAL_GRANTED]: {
    label: 'Approval Granted',
    icon: CheckCircle2,
    color: 'text-green-500',
    bgColor: 'bg-green-100 dark:bg-green-900',
  },
  [EventType.APPROVAL_DENIED]: {
    label: 'Approval Denied',
    icon: XCircle,
    color: 'text-red-500',
    bgColor: 'bg-red-100 dark:bg-red-900',
  },
  // Project Events
  [EventType.PROJECT_CREATED]: {
    label: 'Project Created',
    icon: Folder,
    color: 'text-teal-500',
    bgColor: 'bg-teal-100 dark:bg-teal-900',
  },
  [EventType.PROJECT_UPDATED]: {
    label: 'Project Updated',
    icon: Folder,
    color: 'text-teal-500',
    bgColor: 'bg-teal-100 dark:bg-teal-900',
  },
  [EventType.PROJECT_ARCHIVED]: {
    label: 'Project Archived',
    icon: Folder,
    color: 'text-gray-500',
    bgColor: 'bg-gray-100 dark:bg-gray-800',
  },
  // Workflow Events
  [EventType.WORKFLOW_STARTED]: {
    label: 'Workflow Started',
    icon: Workflow,
    color: 'text-cyan-500',
    bgColor: 'bg-cyan-100 dark:bg-cyan-900',
  },
  [EventType.WORKFLOW_STEP_COMPLETED]: {
    label: 'Step Completed',
    icon: Workflow,
    color: 'text-cyan-500',
    bgColor: 'bg-cyan-100 dark:bg-cyan-900',
  },
  [EventType.WORKFLOW_COMPLETED]: {
    label: 'Workflow Completed',
    icon: CheckCircle2,
    color: 'text-green-500',
    bgColor: 'bg-green-100 dark:bg-green-900',
  },
  [EventType.WORKFLOW_FAILED]: {
    label: 'Workflow Failed',
    icon: XCircle,
    color: 'text-red-500',
    bgColor: 'bg-red-100 dark:bg-red-900',
  },
};

const FILTER_CATEGORIES = [
  {
    id: 'agent',
    label: 'Agent Actions',
    types: [
      EventType.AGENT_SPAWNED,
      EventType.AGENT_MESSAGE,
      EventType.AGENT_STATUS_CHANGED,
      EventType.AGENT_TERMINATED,
    ],
  },
  {
    id: 'issue',
    label: 'Issues',
    types: [
      EventType.ISSUE_CREATED,
      EventType.ISSUE_UPDATED,
      EventType.ISSUE_ASSIGNED,
      EventType.ISSUE_CLOSED,
    ],
  },
  { id: 'sprint', label: 'Sprints', types: [EventType.SPRINT_STARTED, EventType.SPRINT_COMPLETED] },
  {
    id: 'approval',
    label: 'Approvals',
    types: [EventType.APPROVAL_REQUESTED, EventType.APPROVAL_GRANTED, EventType.APPROVAL_DENIED],
  },
  {
    id: 'workflow',
    label: 'Workflows',
    types: [
      EventType.WORKFLOW_STARTED,
      EventType.WORKFLOW_STEP_COMPLETED,
      EventType.WORKFLOW_COMPLETED,
      EventType.WORKFLOW_FAILED,
    ],
  },
  {
    id: 'project',
    label: 'Projects',
    types: [EventType.PROJECT_CREATED, EventType.PROJECT_UPDATED, EventType.PROJECT_ARCHIVED],
  },
];

// ============================================================================
// Helper Functions
// ============================================================================

function getEventConfig(event: ProjectEvent) {
  const config = EVENT_TYPE_CONFIG[event.type];
  if (config) return config;

  /* istanbul ignore next -- defensive fallbacks for unknown event types */
  // Fallback based on event category
  if (isAgentEvent(event)) {
    return {
      icon: Bot,
      label: event.type,
      color: 'text-blue-500',
      bgColor: 'bg-blue-100 dark:bg-blue-900',
    };
  }
  /* istanbul ignore next -- defensive fallback */
  if (isIssueEvent(event)) {
    return {
      icon: FileText,
      label: event.type,
      color: 'text-green-500',
      bgColor: 'bg-green-100 dark:bg-green-900',
    };
  }
  /* istanbul ignore next -- defensive fallback */
  if (isSprintEvent(event)) {
    return {
      icon: PlayCircle,
      label: event.type,
      color: 'text-indigo-500',
      bgColor: 'bg-indigo-100 dark:bg-indigo-900',
    };
  }
  /* istanbul ignore next -- defensive fallback */
  if (isApprovalEvent(event)) {
    return {
      icon: AlertTriangle,
      label: event.type,
      color: 'text-orange-500',
      bgColor: 'bg-orange-100 dark:bg-orange-900',
    };
  }
  /* istanbul ignore next -- defensive fallback */
  if (isWorkflowEvent(event)) {
    return {
      icon: Workflow,
      label: event.type,
      color: 'text-cyan-500',
      bgColor: 'bg-cyan-100 dark:bg-cyan-900',
    };
  }
  /* istanbul ignore next -- defensive fallback */
  if (isProjectEvent(event)) {
    return {
      icon: Folder,
      label: event.type,
      color: 'text-teal-500',
      bgColor: 'bg-teal-100 dark:bg-teal-900',
    };
  }

  /* istanbul ignore next -- defensive fallback for completely unknown events */
  return {
    icon: Activity,
    label: event.type,
    color: 'text-gray-500',
    bgColor: 'bg-gray-100 dark:bg-gray-800',
  };
}

function getEventSummary(event: ProjectEvent): string {
  const payload = event.payload as Record<string, unknown>;

  switch (event.type) {
    /* istanbul ignore next -- AGENT_SPAWNED tested via EventList */
    case EventType.AGENT_SPAWNED:
      return `${payload.agent_name || 'Agent'} spawned as ${payload.role || 'unknown role'}`;
    case EventType.AGENT_MESSAGE:
      return String(payload.message || 'No message');
    /* istanbul ignore next -- rarely used in ActivityFeed tests */
    case EventType.AGENT_STATUS_CHANGED:
      return `Status: ${payload.previous_status} -> ${payload.new_status}`;
    /* istanbul ignore next -- rarely used in ActivityFeed tests */
    case EventType.AGENT_TERMINATED:
      return payload.termination_reason ? String(payload.termination_reason) : 'Agent terminated';
    case EventType.ISSUE_CREATED:
      return String(payload.title || 'New issue created');
    /* istanbul ignore next -- rarely used in ActivityFeed tests */
    case EventType.ISSUE_UPDATED:
      return `Issue ${payload.issue_id || ''} updated`;
    /* istanbul ignore next -- rarely used in ActivityFeed tests */
    case EventType.ISSUE_ASSIGNED:
      return payload.assignee_name
        ? `Assigned to ${payload.assignee_name}`
        : 'Issue assignment changed';
    /* istanbul ignore next -- rarely used in ActivityFeed tests */
    case EventType.ISSUE_CLOSED:
      return payload.resolution ? `Closed: ${payload.resolution}` : 'Issue closed';
    case EventType.SPRINT_STARTED:
      return payload.sprint_name ? `Sprint "${payload.sprint_name}" started` : 'Sprint started';
    /* istanbul ignore next -- rarely used in ActivityFeed tests */
    case EventType.SPRINT_COMPLETED:
      return payload.sprint_name ? `Sprint "${payload.sprint_name}" completed` : 'Sprint completed';
    case EventType.APPROVAL_REQUESTED:
      return String(payload.description || 'Approval requested');
    /* istanbul ignore next -- rarely used in ActivityFeed tests */
    case EventType.APPROVAL_GRANTED:
      return 'Approval granted';
    /* istanbul ignore next -- rarely used in ActivityFeed tests */
    case EventType.APPROVAL_DENIED:
      return payload.reason ? `Denied: ${payload.reason}` : 'Approval denied';
    /* istanbul ignore next -- rarely used in ActivityFeed tests */
    case EventType.WORKFLOW_STARTED:
      return payload.workflow_type
        ? `${payload.workflow_type} workflow started`
        : 'Workflow started';
    /* istanbul ignore next -- rarely used in ActivityFeed tests */
    case EventType.WORKFLOW_STEP_COMPLETED:
      return `Step ${payload.step_number}/${payload.total_steps}: ${payload.step_name || 'completed'}`;
    case EventType.WORKFLOW_COMPLETED:
      return payload.duration_seconds
        ? `Completed in ${payload.duration_seconds}s`
        : 'Workflow completed';
    /* istanbul ignore next -- rarely used in ActivityFeed tests */
    case EventType.WORKFLOW_FAILED:
      return payload.error_message ? String(payload.error_message) : 'Workflow failed';
    /* istanbul ignore next -- defensive fallback */
    default:
      return event.type;
  }
}

function groupEventsByTimePeriod(events: ProjectEvent[]): EventGroup[] {
  const groups: EventGroup[] = [
    { label: 'Today', events: [] },
    { label: 'Yesterday', events: [] },
    { label: 'This Week', events: [] },
    { label: 'Older', events: [] },
  ];

  events.forEach((event) => {
    const eventDate = new Date(event.timestamp);

    if (isToday(eventDate)) {
      groups[0].events.push(event);
    } else if (isYesterday(eventDate)) {
      groups[1].events.push(event);
    } else if (isThisWeek(eventDate, { weekStartsOn: 1 })) {
      groups[2].events.push(event);
    } else {
      groups[3].events.push(event);
    }
  });

  // Sort events within each group by timestamp (newest first)
  groups.forEach((group) => {
    group.events.sort((a, b) => new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime());
  });

  return groups.filter((g) => g.events.length > 0);
}

function formatActorDisplay(event: ProjectEvent): string {
  if (event.actor_type === 'system') return 'System';
  if (event.actor_type === 'agent') return 'Agent';
  if (event.actor_type === 'user') return 'User';
  /* istanbul ignore next -- defensive fallback for unknown actor types */
  return event.actor_type;
}

// ============================================================================
// Sub-Components
// ============================================================================

interface ConnectionIndicatorProps {
  state: ConnectionState;
  onReconnect?: () => void;
  className?: string;
}

function ConnectionIndicator({ state, onReconnect, className }: ConnectionIndicatorProps) {
  const statusConfig = {
    connected: { color: 'bg-green-500', label: 'Live', pulse: true },
    connecting: { color: 'bg-yellow-500', label: 'Connecting...', pulse: true },
    disconnected: { color: 'bg-gray-400', label: 'Disconnected', pulse: false },
    error: { color: 'bg-red-500', label: 'Error', pulse: false },
  };

  const config = statusConfig[state];
  const canReconnect = state === 'disconnected' || state === 'error';

  return (
    <div className={cn('flex items-center gap-2', className)} data-testid="connection-indicator">
      <span
        className={cn('h-2 w-2 rounded-full', config.color, config.pulse && 'animate-pulse')}
        aria-hidden="true"
      />
      <span className="text-sm text-muted-foreground">{config.label}</span>
      {canReconnect && onReconnect && (
        <Button
          variant="ghost"
          size="sm"
          onClick={onReconnect}
          className="h-6 w-6 p-0"
          aria-label="Reconnect"
        >
          <RefreshCw className="h-3 w-3" />
        </Button>
      )}
    </div>
  );
}

interface FilterPanelProps {
  selectedCategories: string[];
  onCategoryChange: (categoryId: string) => void;
  showPendingOnly: boolean;
  onShowPendingOnlyChange: (value: boolean) => void;
  onClearFilters: () => void;
  events: ProjectEvent[];
}

function FilterPanel({
  selectedCategories,
  onCategoryChange,
  showPendingOnly,
  onShowPendingOnlyChange,
  onClearFilters,
  events,
}: FilterPanelProps) {
  const getCategoryCount = (types: EventType[]) => {
    return events.filter((e) => types.includes(e.type)).length;
  };

  const pendingCount = events.filter((e) => e.type === EventType.APPROVAL_REQUESTED).length;

  return (
    <Card className="p-4" data-testid="filter-panel">
      <div className="space-y-4">
        <div>
          <Label className="text-sm font-medium">Event Types</Label>
          <div className="mt-2 grid grid-cols-2 gap-2">
            {FILTER_CATEGORIES.map((category) => {
              const count = getCategoryCount(category.types);
              return (
                <div key={category.id} className="flex items-center gap-2">
                  <Checkbox
                    id={`filter-${category.id}`}
                    checked={selectedCategories.includes(category.id)}
                    onCheckedChange={() => onCategoryChange(category.id)}
                  />
                  <Label
                    htmlFor={`filter-${category.id}`}
                    className="flex items-center gap-1 text-sm font-normal cursor-pointer"
                  >
                    {category.label}
                    <Badge variant="secondary" className="text-xs">
                      {count}
                    </Badge>
                  </Label>
                </div>
              );
            })}
          </div>
        </div>

        <Separator />

        <div className="flex items-center gap-2">
          <Checkbox
            id="filter-pending"
            checked={showPendingOnly}
            onCheckedChange={(checked) => onShowPendingOnlyChange(checked as boolean)}
          />
          <Label
            htmlFor="filter-pending"
            className="flex items-center gap-1 text-sm font-normal cursor-pointer"
          >
            Show only pending approvals
            {pendingCount > 0 && (
              <Badge variant="destructive" className="text-xs">
                {pendingCount}
              </Badge>
            )}
          </Label>
        </div>

        <div className="flex justify-end">
          <Button variant="ghost" size="sm" onClick={onClearFilters}>
            Clear Filters
          </Button>
        </div>
      </div>
    </Card>
  );
}

interface EventItemProps {
  event: ProjectEvent;
  expanded: boolean;
  onToggle: () => void;
  onApprove?: (event: ProjectEvent) => void;
  onReject?: (event: ProjectEvent) => void;
  onClick?: (event: ProjectEvent) => void;
  compact?: boolean;
}

function EventItem({
  event,
  expanded,
  onToggle,
  onApprove,
  onReject,
  onClick,
  compact = false,
}: EventItemProps) {
  const config = getEventConfig(event);
  const Icon = config.icon;
  const summary = getEventSummary(event);
  const actor = formatActorDisplay(event);
  const timestamp = formatDistanceToNow(new Date(event.timestamp), { addSuffix: true });
  const isPendingApproval = event.type === EventType.APPROVAL_REQUESTED;
  const payload = event.payload as Record<string, unknown>;

  const handleClick = () => {
    onClick?.(event);
    onToggle();
  };

  const handleApprove = (e: React.MouseEvent) => {
    e.stopPropagation();
    onApprove?.(event);
  };

  const handleReject = (e: React.MouseEvent) => {
    e.stopPropagation();
    onReject?.(event);
  };

  return (
    <div
      className={cn(
        'border-b border-border/50 last:border-b-0 transition-colors',
        'cursor-pointer hover:bg-muted/50',
        isPendingApproval && 'border-l-4 border-l-orange-500',
        compact ? 'p-2' : 'p-4'
      )}
      onClick={handleClick}
      role="button"
      tabIndex={0}
      onKeyDown={(e) => {
        if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault();
          handleClick();
        }
      }}
      data-testid={`event-item-${event.id}`}
    >
      <div className="flex gap-3">
        {/* Icon */}
        <div
          className={cn(
            'flex shrink-0 items-center justify-center rounded-full',
            config.bgColor,
            compact ? 'h-8 w-8' : 'h-10 w-10'
          )}
        >
          <Icon className={cn(config.color, compact ? 'h-4 w-4' : 'h-5 w-5')} aria-hidden="true" />
        </div>

        {/* Content */}
        <div className="flex-1 min-w-0">
          <div className="flex items-start justify-between gap-2">
            <div className="flex-1 min-w-0">
              <div className="flex flex-wrap items-center gap-2">
                <Badge variant="outline" className="text-xs">
                  {config.label}
                </Badge>
                <span className="text-xs text-muted-foreground">{actor}</span>
                {isPendingApproval && (
                  <Badge variant="destructive" className="text-xs">
                    Action Required
                  </Badge>
                )}
              </div>
              <p className={cn('mt-1 text-sm', compact && 'truncate')}>{summary}</p>
            </div>

            <div className="flex items-center gap-1 shrink-0">
              <span className="text-xs text-muted-foreground whitespace-nowrap">{timestamp}</span>
              <Button
                variant="ghost"
                size="sm"
                className="h-6 w-6 p-0"
                /* istanbul ignore next -- click handler tested via parent element */
                onClick={(e) => {
                  e.stopPropagation();
                  onToggle();
                }}
                aria-label={expanded ? 'Collapse details' : 'Expand details'}
              >
                {expanded ? (
                  <ChevronDown className="h-4 w-4" />
                ) : (
                  <ChevronRight className="h-4 w-4" />
                )}
              </Button>
            </div>
          </div>

          {/* Expanded Details */}
          {expanded &&
            (() => {
              const issueId = payload.issue_id as string | undefined;
              const pullRequest = payload.pullRequest as string | number | undefined;
              const documentUrl = payload.documentUrl as string | undefined;
              const progress = payload.progress as number | undefined;

              return (
                <div
                  className="mt-3 rounded-md bg-muted/50 p-3 space-y-3"
                  data-testid="event-details"
                >
                  {/* Issue/PR Links */}
                  {issueId && (
                    <div className="flex items-center gap-2 text-sm">
                      <CircleDot className="h-4 w-4" aria-hidden="true" />
                      <span>Issue #{issueId}</span>
                    </div>
                  )}
                  {pullRequest && (
                    <div className="flex items-center gap-2 text-sm">
                      <GitPullRequest className="h-4 w-4" aria-hidden="true" />
                      <span>PR #{String(pullRequest)}</span>
                    </div>
                  )}

                  {/* Document Links */}
                  {documentUrl && (
                    <div className="flex items-center gap-2 text-sm">
                      <ExternalLink className="h-4 w-4" aria-hidden="true" />
                      <a href={documentUrl} className="text-primary hover:underline">
                        {documentUrl}
                      </a>
                    </div>
                  )}

                  {/* Progress */}
                  {progress !== undefined && (
                    <div className="space-y-1">
                      <div className="flex justify-between text-sm">
                        <span>Progress</span>
                        <span>{progress}%</span>
                      </div>
                      <div className="h-2 rounded-full bg-muted">
                        <div
                          className="h-full rounded-full bg-primary"
                          style={{ width: `${progress}%` }}
                        />
                      </div>
                    </div>
                  )}

                  {/* Timestamp */}
                  <p className="text-xs text-muted-foreground">
                    {new Date(event.timestamp).toLocaleString()}
                  </p>

                  {/* Raw Payload (for debugging) */}
                  <details className="text-xs">
                    <summary className="cursor-pointer text-muted-foreground hover:text-foreground">
                      View raw payload
                    </summary>
                    <pre className="mt-2 overflow-x-auto whitespace-pre-wrap break-words rounded bg-muted p-2">
                      {JSON.stringify(event.payload, null, 2)}
                    </pre>
                  </details>
                </div>
              );
            })()}

          {/* Approval Actions */}
          {isPendingApproval && (onApprove || onReject) && (
            <div className="mt-3 flex gap-2">
              {onApprove && (
                <Button size="sm" onClick={handleApprove} data-testid="approve-button">
                  <CheckCircle2 className="mr-2 h-4 w-4" />
                  Approve
                </Button>
              )}
              {onReject && (
                <Button
                  variant="outline"
                  size="sm"
                  onClick={handleReject}
                  data-testid="reject-button"
                >
                  <XCircle className="mr-2 h-4 w-4" />
                  Reject
                </Button>
              )}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

function LoadingSkeleton() {
  return (
    <div className="space-y-4" data-testid="loading-skeleton">
      {[1, 2, 3].map((i) => (
        <div key={i} className="flex gap-3 p-4">
          <Skeleton className="h-10 w-10 rounded-full" />
          <div className="flex-1 space-y-2">
            <Skeleton className="h-4 w-24" />
            <Skeleton className="h-4 w-full" />
            <Skeleton className="h-4 w-3/4" />
          </div>
        </div>
      ))}
    </div>
  );
}

function EmptyState({ hasFilters }: { hasFilters: boolean }) {
  return (
    <div
      className="flex flex-col items-center justify-center py-12 text-muted-foreground"
      data-testid="empty-state"
    >
      <Activity className="h-12 w-12 mb-4" aria-hidden="true" />
      <h3 className="font-semibold">No activity found</h3>
      <p className="text-sm">
        {hasFilters
          ? 'Try adjusting your search or filters'
          : 'Activity will appear here as agents work on your projects'}
      </p>
    </div>
  );
}

// ============================================================================
// Main Component
// ============================================================================

export function ActivityFeed({
  events,
  connectionState,
  isLoading = false,
  onReconnect,
  onApprove,
  onReject,
  onEventClick,
  maxHeight = 'auto',
  showHeader = true,
  title = 'Activity Feed',
  enableFiltering = true,
  enableSearch = true,
  compact = false,
  className,
}: ActivityFeedProps) {
  // State
  const [expandedEvents, setExpandedEvents] = useState<Set<string>>(new Set());
  const [searchQuery, setSearchQuery] = useState('');
  const [showFilters, setShowFilters] = useState(false);
  const [selectedCategories, setSelectedCategories] = useState<string[]>([]);
  const [showPendingOnly, setShowPendingOnly] = useState(false);

  // Filter logic
  const filteredEvents = useMemo(() => {
    let result = events;

    // Search filter
    if (searchQuery) {
      const query = searchQuery.toLowerCase();
      result = result.filter((event) => {
        const summary = getEventSummary(event).toLowerCase();
        return summary.includes(query) || event.type.toLowerCase().includes(query);
      });
    }

    // Category filter
    if (selectedCategories.length > 0) {
      const allowedTypes = new Set<EventType>();
      selectedCategories.forEach((categoryId) => {
        const category = FILTER_CATEGORIES.find((c) => c.id === categoryId);
        if (category) {
          category.types.forEach((type) => allowedTypes.add(type));
        }
      });
      result = result.filter((event) => allowedTypes.has(event.type));
    }

    // Pending only filter
    if (showPendingOnly) {
      result = result.filter((event) => event.type === EventType.APPROVAL_REQUESTED);
    }

    return result;
  }, [events, searchQuery, selectedCategories, showPendingOnly]);

  // Group events by time
  const groupedEvents = useMemo(() => groupEventsByTimePeriod(filteredEvents), [filteredEvents]);

  // Event handlers
  const toggleExpanded = useCallback((eventId: string) => {
    setExpandedEvents((prev) => {
      const next = new Set(prev);
      if (next.has(eventId)) {
        next.delete(eventId);
      } else {
        next.add(eventId);
      }
      return next;
    });
  }, []);

  const handleCategoryChange = useCallback((categoryId: string) => {
    setSelectedCategories((prev) =>
      prev.includes(categoryId) ? prev.filter((c) => c !== categoryId) : [...prev, categoryId]
    );
  }, []);

  const handleClearFilters = useCallback(() => {
    setSelectedCategories([]);
    setShowPendingOnly(false);
    setSearchQuery('');
  }, []);

  const hasFilters = searchQuery !== '' || selectedCategories.length > 0 || showPendingOnly;
  const pendingCount = events.filter((e) => e.type === EventType.APPROVAL_REQUESTED).length;

  return (
    <Card className={className} data-testid="activity-feed">
      {showHeader && (
        <CardHeader className="pb-3">
          <div className="flex items-center justify-between">
            <CardTitle className="text-lg">{title}</CardTitle>
            <div className="flex items-center gap-3">
              <ConnectionIndicator state={connectionState} onReconnect={onReconnect} />
              {pendingCount > 0 && (
                <Badge variant="destructive" className="gap-1">
                  <AlertTriangle className="h-3 w-3" />
                  {pendingCount} pending
                </Badge>
              )}
            </div>
          </div>
        </CardHeader>
      )}

      <CardContent className={showHeader ? 'pt-0' : ''}>
        <div className="space-y-4">
          {/* Search and Filter Controls */}
          {(enableSearch || enableFiltering) && (
            <div className="flex flex-col gap-4 sm:flex-row">
              {enableSearch && (
                <div className="relative flex-1">
                  <Search className="absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground" />
                  <Input
                    placeholder="Search activity..."
                    value={searchQuery}
                    onChange={(e) => setSearchQuery(e.target.value)}
                    className="pl-9"
                    data-testid="search-input"
                  />
                </div>
              )}
              {enableFiltering && (
                <Button
                  variant="outline"
                  onClick={() => setShowFilters(!showFilters)}
                  className={showFilters ? 'bg-muted' : ''}
                  data-testid="filter-toggle"
                >
                  <Filter className="mr-2 h-4 w-4" />
                  Filters
                  {hasFilters && (
                    <Badge variant="secondary" className="ml-2">
                      Active
                    </Badge>
                  )}
                </Button>
              )}
            </div>
          )}

          {/* Filter Panel */}
          {showFilters && enableFiltering && (
            <FilterPanel
              selectedCategories={selectedCategories}
              onCategoryChange={handleCategoryChange}
              showPendingOnly={showPendingOnly}
              onShowPendingOnlyChange={setShowPendingOnly}
              onClearFilters={handleClearFilters}
              events={events}
            />
          )}

          {/* Event List */}
          <div
            className="overflow-y-auto"
            style={{ maxHeight: typeof maxHeight === 'number' ? `${maxHeight}px` : maxHeight }}
          >
            {isLoading ? (
              <LoadingSkeleton />
            ) : filteredEvents.length === 0 ? (
              <EmptyState hasFilters={hasFilters} />
            ) : (
              <div className="space-y-6">
                {groupedEvents.map((group) => (
                  <div
                    key={group.label}
                    data-testid={`event-group-${group.label.toLowerCase().replace(' ', '-')}`}
                  >
                    <div className="mb-3 flex items-center gap-2">
                      <h3 className="text-sm font-medium text-muted-foreground">{group.label}</h3>
                      <Badge variant="secondary" className="text-xs">
                        {group.events.length}
                      </Badge>
                      <Separator className="flex-1" />
                    </div>
                    <div className="rounded-lg border divide-y divide-border/50">
                      {group.events.map((event) => (
                        <EventItem
                          key={event.id}
                          event={event}
                          expanded={expandedEvents.has(event.id)}
                          onToggle={() => toggleExpanded(event.id)}
                          onApprove={onApprove}
                          onReject={onReject}
                          onClick={onEventClick}
                          compact={compact}
                        />
                      ))}
                    </div>
                  </div>
                ))}
              </div>
            )}
          </div>
        </div>
      </CardContent>
    </Card>
  );
}
