import { useState, useCallback, useMemo, memo, useEffect } from "react";
import { Link, useSearchParams } from "react-router-dom";
import { useQueryClient } from "@tanstack/react-query";
import { useEvents } from "@/hooks/useEvents";
import {
  useEntityNotifications,
  Notification,
} from "@/contexts/WebSocketContext";
import { Search, X } from "lucide-react";
import AutocompleteInput from "@/components/common/AutocompleteInput";
import {
  useFilterSuggestions,
  useMergedSuggestions,
} from "@/hooks/useFilterSuggestions";
import type { EventSummary } from "@/api";

const formatDate = (dateString: string) => {
  return new Date(dateString).toLocaleString();
};

const formatTime = (timestamp: string) => {
  const date = new Date(timestamp);
  const now = new Date();
  const diff = now.getTime() - date.getTime();

  if (diff < 60000) return "just now";
  if (diff < 3600000) return `${Math.floor(diff / 60000)}m ago`;
  if (diff < 86400000) return `${Math.floor(diff / 3600000)}h ago`;
  return date.toLocaleDateString();
};

// Memoized results table component - only re-renders when query data changes,
// NOT when the user types in filter inputs.
const EventsResultsTable = memo(
  ({
    events,
    isLoading,
    isFetching,
    error,
    hasActiveFilters,
    clearFilters,
    page,
    setPage,
    pageSize,
    total,
  }: {
    events: EventSummary[];
    isLoading: boolean;
    isFetching: boolean;
    error: Error | null;
    hasActiveFilters: boolean;
    clearFilters: () => void;
    page: number;
    setPage: (page: number) => void;
    pageSize: number;
    total: number;
  }) => {
    const totalPages = total ? Math.ceil(total / pageSize) : 0;

    // Initial load (no cached data yet)
    if (isLoading && events.length === 0) {
      return (
        <div className="bg-white shadow rounded-lg">
          <div className="flex items-center justify-center h-64">
            <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600" />
            <p className="ml-4 text-gray-600">Loading events...</p>
          </div>
        </div>
      );
    }

    // Error with no cached data to show
    if (error && events.length === 0) {
      return (
        <div className="bg-white shadow rounded-lg p-12 text-center">
          <p className="text-red-600">Failed to load events</p>
          <p className="text-sm text-gray-600 mt-2">{error.message}</p>
        </div>
      );
    }

    // Empty results
    if (events.length === 0) {
      return (
        <div className="bg-white shadow rounded-lg p-12 text-center">
          <svg
            className="mx-auto h-12 w-12 text-gray-400"
            fill="none"
            stroke="currentColor"
            viewBox="0 0 24 24"
          >
            <path
              strokeLinecap="round"
              strokeLinejoin="round"
              strokeWidth={2}
              d="M13 10V3L4 14h7v7l9-11h-7z"
            />
          </svg>
          <p className="mt-4 text-gray-600">No events found</p>
          <p className="text-sm text-gray-500 mt-1">
            {hasActiveFilters
              ? "Try adjusting your filters"
              : "Events will appear here when triggers fire"}
          </p>
          {hasActiveFilters && (
            <button
              onClick={clearFilters}
              className="mt-3 text-sm text-blue-600 hover:text-blue-800"
            >
              Clear filters
            </button>
          )}
        </div>
      );
    }

    return (
      <div className="relative">
        {/* Inline loading overlay - shows on top of previous results while fetching */}
        {isFetching && (
          <div className="absolute inset-0 bg-white/60 z-10 flex items-center justify-center rounded-lg">
            <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600" />
          </div>
        )}

        {/* Non-fatal error banner (data still shown from cache) */}
        {error && (
          <div className="mb-4 bg-red-50 border border-red-200 text-red-700 px-4 py-3 rounded">
            <p>Error refreshing: {error.message}</p>
          </div>
        )}

        <div className="bg-white shadow rounded-lg overflow-hidden">
          <div className="overflow-x-auto">
            <table className="min-w-full divide-y divide-gray-200">
              <thead className="bg-gray-50">
                <tr>
                  <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                    ID
                  </th>
                  <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                    Trigger
                  </th>
                  <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                    Rule
                  </th>
                  <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                    Source
                  </th>
                  <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                    Created
                  </th>
                </tr>
              </thead>
              <tbody className="bg-white divide-y divide-gray-200">
                {events.map((event) => (
                  <tr key={event.id} className="hover:bg-gray-50">
                    <td className="px-6 py-4 whitespace-nowrap">
                      <Link
                        to={`/events/${event.id}`}
                        className="text-sm font-mono text-blue-600 hover:text-blue-800"
                      >
                        #{event.id}
                      </Link>
                    </td>
                    <td className="px-6 py-4">
                      <div className="text-sm">
                        <div className="font-medium text-gray-900">
                          {event.trigger_ref}
                        </div>
                        <div className="text-gray-500 text-xs">
                          ID: {event.trigger || "N/A"}
                        </div>
                      </div>
                    </td>
                    <td className="px-6 py-4">
                      {event.rule_ref ? (
                        <div className="text-sm">
                          <Link
                            to={`/rules/${event.rule}`}
                            className="font-medium text-blue-600 hover:text-blue-900"
                          >
                            {event.rule_ref}
                          </Link>
                          <div className="text-gray-500 text-xs">
                            ID: {event.rule}
                          </div>
                        </div>
                      ) : (
                        <span className="text-sm text-gray-400 italic">
                          No rule
                        </span>
                      )}
                    </td>
                    <td className="px-6 py-4">
                      {event.source_ref ? (
                        <div className="text-sm">
                          <div className="font-medium text-gray-900">
                            {event.source_ref}
                          </div>
                          <div className="text-gray-500 text-xs">
                            ID: {event.source || "N/A"}
                          </div>
                        </div>
                      ) : (
                        <span className="text-sm text-gray-400 italic">
                          No source
                        </span>
                      )}
                    </td>
                    <td className="px-6 py-4 whitespace-nowrap">
                      <div className="text-sm text-gray-900">
                        {formatTime(event.created)}
                      </div>
                      <div className="text-xs text-gray-500">
                        {formatDate(event.created)}
                      </div>
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>

        {/* Pagination */}
        {totalPages > 1 && (
          <div className="bg-gray-50 px-6 py-4 flex items-center justify-between border-t border-gray-200">
            <div className="flex-1 flex justify-between sm:hidden">
              <button
                onClick={() => setPage(page - 1)}
                disabled={page === 1}
                className="relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
              >
                Previous
              </button>
              <button
                onClick={() => setPage(page + 1)}
                disabled={page === totalPages}
                className="ml-3 relative inline-flex items-center px-4 py-2 border border-gray-300 text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
              >
                Next
              </button>
            </div>
            <div className="hidden sm:flex-1 sm:flex sm:items-center sm:justify-between">
              <div>
                <p className="text-sm text-gray-700">
                  Page <span className="font-medium">{page}</span> of{" "}
                  <span className="font-medium">{totalPages}</span>
                </p>
              </div>
              <div>
                <nav className="relative z-0 inline-flex rounded-md shadow-sm -space-x-px">
                  <button
                    onClick={() => setPage(page - 1)}
                    disabled={page === 1}
                    className="relative inline-flex items-center px-2 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
                  >
                    Previous
                  </button>
                  <button
                    onClick={() => setPage(page + 1)}
                    disabled={page === totalPages}
                    className="relative inline-flex items-center px-2 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-500 hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
                  >
                    Next
                  </button>
                </nav>
              </div>
            </div>
          </div>
        )}
      </div>
    );
  },
);

EventsResultsTable.displayName = "EventsResultsTable";

export default function EventsPage() {
  const [searchParams] = useSearchParams();
  const queryClient = useQueryClient();

  // --- Filter input state (updates immediately on keystroke) ---
  const [page, setPage] = useState(1);
  const pageSize = 50;
  const [searchFilters, setSearchFilters] = useState({
    trigger: searchParams.get("trigger_ref") || "",
    rule: searchParams.get("rule_ref") || "",
  });

  // --- Debounced filter state (drives API calls, updates after delay) ---
  const [debouncedFilters, setDebouncedFilters] = useState(searchFilters);

  useEffect(() => {
    const timer = setTimeout(() => {
      setDebouncedFilters(searchFilters);
    }, 500);
    return () => clearTimeout(timer);
  }, [searchFilters]);

  // --- Autocomplete suggestions ---
  const baseSuggestions = useFilterSuggestions();

  // Additional refs discovered via WebSocket notifications (accumulated over time)
  const [wsRefs, setWsRefs] = useState<{
    triggers: string[];
    rules: string[];
  }>({ triggers: [], rules: [] });

  // --- Build query params from debounced state ---
  const queryParams = useMemo(() => {
    const params: {
      page: number;
      pageSize: number;
      triggerRef?: string;
      ruleRef?: string;
    } = { page, pageSize };
    if (debouncedFilters.trigger) params.triggerRef = debouncedFilters.trigger;
    if (debouncedFilters.rule) params.ruleRef = debouncedFilters.rule;
    return params;
  }, [page, pageSize, debouncedFilters]);

  // Set up WebSocket for real-time event updates with stable callback
  const handleEventNotification = useCallback(
    (notification: Notification) => {
      if (notification.notification_type === "event_created") {
        const payload = notification.payload as Partial<EventSummary> & {
          payload?: unknown;
        };

        const newEvent: EventSummary = {
          id: payload.id ?? 0,
          trigger: payload.trigger ?? 0,
          trigger_ref: payload.trigger_ref ?? "",
          rule: payload.rule,
          rule_ref: payload.rule_ref,
          source: payload.source,
          source_ref: payload.source_ref,
          has_payload:
            payload.payload !== null && payload.payload !== undefined,
          created: payload.created ?? new Date().toISOString(),
        };

        // Augment autocomplete suggestions with new refs from notification
        setWsRefs((prev) => {
          const newTriggers = new Set(prev.triggers);
          const newRules = new Set(prev.rules);
          let changed = false;

          if (newEvent.trigger_ref && !newTriggers.has(newEvent.trigger_ref)) {
            newTriggers.add(newEvent.trigger_ref);
            changed = true;
          }
          if (newEvent.rule_ref && !newRules.has(newEvent.rule_ref)) {
            newRules.add(newEvent.rule_ref);
            changed = true;
          }

          if (!changed) return prev;
          return {
            triggers: [...newTriggers],
            rules: [...newRules],
          };
        });

        queryClient.setQueryData(
          ["events", queryParams],
          (
            oldData:
              | {
                  data: EventSummary[];
                  pagination?: { total_items?: number };
                }
              | undefined,
          ) => {
            if (!oldData) return oldData;

            // Check if filtering and event matches filter
            if (
              debouncedFilters.trigger &&
              newEvent.trigger_ref !== debouncedFilters.trigger
            ) {
              return oldData;
            }
            if (
              debouncedFilters.rule &&
              newEvent.rule_ref !== debouncedFilters.rule
            ) {
              return oldData;
            }

            // Add new event to the beginning of the list if on first page
            if (page === 1) {
              return {
                ...oldData,
                data: [newEvent, ...oldData.data].slice(0, pageSize),
                pagination: {
                  ...oldData.pagination,
                  total_items: (oldData.pagination?.total_items || 0) + 1,
                },
              };
            }

            // For other pages, just update the total count
            return {
              ...oldData,
              pagination: {
                ...oldData.pagination,
                total_items: (oldData.pagination?.total_items || 0) + 1,
              },
            };
          },
        );
      }
    },
    [queryClient, queryParams, page, pageSize, debouncedFilters],
  );

  const { connected: wsConnected } = useEntityNotifications(
    "event",
    handleEventNotification,
  );

  const { data, isLoading, isFetching, error } = useEvents(queryParams);

  const events = useMemo(() => data?.data || [], [data]);
  const total = data?.pagination?.total_items || 0;

  // Derive refs from currently-loaded event data (no setState needed)
  const loadedRefs = useMemo(() => {
    const triggers = new Set<string>();
    const rules = new Set<string>();

    for (const event of events) {
      if (event.trigger_ref) triggers.add(event.trigger_ref);
      if (event.rule_ref) rules.add(event.rule_ref);
    }

    return {
      triggers: [...triggers],
      rules: [...rules],
    };
  }, [events]);

  // Merge base entity suggestions + loaded data refs + WebSocket refs
  const triggerSuggestions = useMergedSuggestions(
    baseSuggestions.triggerRefs,
    loadedRefs.triggers,
    wsRefs.triggers,
  );
  const ruleSuggestions = useMergedSuggestions(
    baseSuggestions.ruleRefs,
    loadedRefs.rules,
    wsRefs.rules,
  );

  const handleFilterChange = useCallback((field: string, value: string) => {
    setSearchFilters((prev) => ({ ...prev, [field]: value }));
    setPage(1);
  }, []);

  const clearFilters = useCallback(() => {
    setSearchFilters({ trigger: "", rule: "" });
    setPage(1);
  }, []);

  const hasActiveFilters = Object.values(searchFilters).some((v) => v !== "");

  return (
    <div className="p-6">
      {/* Header - always visible */}
      <div className="mb-6">
        <div className="flex items-center justify-between">
          <div>
            <h1 className="text-3xl font-bold text-gray-900">Events</h1>
            <p className="mt-2 text-gray-600">
              Event instances generated by sensors and triggers
            </p>
            {isFetching && hasActiveFilters && (
              <p className="text-sm text-gray-500 mt-1">Searching events...</p>
            )}
          </div>
          {wsConnected && (
            <div className="flex items-center gap-2 text-sm text-green-600">
              <div className="w-2 h-2 bg-green-600 rounded-full animate-pulse"></div>
              <span>Live updates</span>
            </div>
          )}
        </div>
      </div>

      {/* Filter section - always mounted, never unmounts during loading */}
      <div className="bg-white shadow rounded-lg p-4 mb-6">
        <div className="flex items-center justify-between mb-4">
          <div className="flex items-center gap-2">
            <Search className="h-5 w-5 text-gray-400" />
            <h2 className="text-lg font-semibold">Filter Events</h2>
          </div>
          {hasActiveFilters && (
            <button
              onClick={clearFilters}
              className="flex items-center gap-1 text-sm text-gray-600 hover:text-gray-900"
            >
              <X className="h-4 w-4" />
              Clear Filters
            </button>
          )}
        </div>
        <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
          <AutocompleteInput
            label="Trigger"
            value={searchFilters.trigger}
            onChange={(value) => handleFilterChange("trigger", value)}
            suggestions={triggerSuggestions}
            placeholder="e.g., core.webhook"
          />
          <AutocompleteInput
            label="Rule"
            value={searchFilters.rule}
            onChange={(value) => handleFilterChange("rule", value)}
            suggestions={ruleSuggestions}
            placeholder="e.g., core.on_webhook"
          />
        </div>
        {data && (
          <div className="mt-3 text-sm text-gray-600">
            Showing {events.length} of {total} events
            {hasActiveFilters && " (filtered)"}
          </div>
        )}
      </div>

      {/* Results section - isolated from filter state, only depends on query results */}
      <EventsResultsTable
        events={events}
        isLoading={isLoading}
        isFetching={isFetching}
        error={error as Error | null}
        hasActiveFilters={hasActiveFilters}
        clearFilters={clearFilters}
        page={page}
        setPage={setPage}
        pageSize={pageSize}
        total={total}
      />
    </div>
  );
}
