import { useCallback, useMemo } from "react";
import { useSearchParams, useNavigate } from "react-router-dom";
import { toast } from "sonner";
import { useActivity } from "@/hooks/useActivity";
import { useTailStream } from "@/hooks/useTailStream";
import { useMergedTail } from "@/hooks/useMergedTail";
import { useRemitDrawerUrlState } from "@/hooks/useRemitDrawerUrlState";
import { eventKindToUrl } from "@/lib/event-routing";
import { TailStatusPill } from "@/components/TailStatusPill";
import { PageHeader } from "@/components/PageHeader";
import { ActivityFeed } from "@/components/ActivityFeed";
import { ActivityFilters, type SinceValue } from "@/components/ActivityFilters";
import { RemitDrawer } from "@/components/RemitDrawer";
import { Skeleton } from "@/components/ui/skeleton";
import { EmptyState } from "@/components/ui/empty-state";
import { ErrorState } from "@/components/ui/error-state";
import type { ActivityKind } from "@/types";

const VALID_KINDS: readonly ActivityKind[] = [
  "claim_submitted",
  "claim_paid",
  "claim_denied",
  "claim_accepted",
  "remit_received",
  "provider_added",
];

const VALID_SINCE: readonly SinceValue[] = ["1h", "24h", "7d", "all"];

function useSinceIso(since: SinceValue): string | undefined {
  return useMemo(() => {
    const now = Date.now();
    if (since === "1h") return new Date(now - 60 * 60 * 1000).toISOString();
    if (since === "24h") return new Date(now - 24 * 60 * 60 * 1000).toISOString();
    if (since === "7d") return new Date(now - 7 * 24 * 60 * 60 * 1000).toISOString();
    return undefined;
  }, [since]);
}

export function ActivityLog() {
  const [searchParams, setSearchParams] = useSearchParams();
  const navigate = useNavigate();
  // SP21 Phase 4 Task 4.7: `remit_received` events with a
  // remittanceId drill into the RemitDrawer. Calling `open(id)`
  // pushes `?remit=ID` onto the current URL (no navigation away
  // from `/activity`), so the activity feed stays visible behind
  // the drawer and the drawer portals in over it.
  const { remitId, open, close } = useRemitDrawerUrlState();

  const selectedKinds = useMemo<ActivityKind[]>(
    () =>
      searchParams
        .getAll("kind")
        .filter((k): k is ActivityKind =>
          (VALID_KINDS as readonly string[]).includes(k),
        ),
    [searchParams],
  );

  const sinceRaw = searchParams.get("since") ?? "all";
  const since: SinceValue = (VALID_SINCE as readonly string[]).includes(sinceRaw)
    ? (sinceRaw as SinceValue)
    : "all";

  const apiKind = selectedKinds.length === 1 ? selectedKinds[0] : undefined;
  const sinceIso = useSinceIso(since);

  const { data, isLoading, isError, error, refetch } = useActivity({
    kind: apiKind,
    since: sinceIso,
    // 500 keeps the wire size manageable (and the operator can rely
    // on the live tail to surface new events as they land). The
    // activity list endpoint doesn't expose a true total — `data.total`
    // reflects events matching the current kind/since filter, capped
    // at the request limit — so the eyebrow below uses "Showing N
    // most recent" instead of "X of Y" to avoid a misleading ratio.
    limit: 500,
  });

  const allItems = data?.items ?? [];

  const { status: tailStatus, lastEventAt: tailLastEventAt, forceReconnect } =
    useTailStream("activity");
  const merged = useMergedTail("activity", allItems);
  const items = useMemo(() => {
    if (selectedKinds.length <= 1) return merged;
    return merged.filter((a) => selectedKinds.includes(a.kind));
  }, [merged, selectedKinds]);

  const writeParams = useCallback(
    (mutate: (next: URLSearchParams) => void) => {
      setSearchParams(
        (prev) => {
          const next = new URLSearchParams(prev);
          mutate(next);
          return next;
        },
        { replace: true },
      );
    },
    [setSearchParams],
  );

  const setSelectedKinds = useCallback(
    (kinds: ActivityKind[]) => {
      writeParams((next) => {
        next.delete("kind");
        for (const k of kinds) next.append("kind", k);
      });
    },
    [writeParams],
  );

  const setSince = useCallback(
    (value: SinceValue) => {
      writeParams((next) => {
        if (value === "all") next.delete("since");
        else next.set("since", value);
      });
    },
    [writeParams],
  );

  const clearFilters = useCallback(() => {
    writeParams((next) => {
      next.delete("kind");
      next.delete("since");
    });
  }, [writeParams]);

  const hasFilters = selectedKinds.length > 0 || since !== "all";

  return (
    <div className="space-y-6 lg:space-y-8 animate-fade-in">
      <PageHeader
        eyebrow={
          allItems.length > 0
            ? `Activity · showing ${allItems.length.toLocaleString()} most recent`
            : "Activity"
        }
        title={<>Activity <span className="display italic text-muted-foreground">log</span></>}
        subtitle="Every claim submission, denial, payment, and provider event, in reverse chronological order. Auto-refreshes every 30s."
        actions={
          <TailStatusPill
            status={tailStatus}
            lastEventAt={tailLastEventAt}
            onReconnect={forceReconnect}
          />
        }
      />

      <div className="surface rounded-xl p-4">
        <ActivityFilters
          selectedKinds={selectedKinds}
          onKindsChange={setSelectedKinds}
          since={since}
          onSinceChange={setSince}
          onClear={clearFilters}
        />
      </div>

      {isError ? (
        <ErrorState
          message="Couldn't load activity from the backend."
          detail={error instanceof Error ? error.message : String(error)}
          onRetry={() => refetch()}
        />
      ) : null}

      <div className="surface rounded-xl p-6">
        {isLoading ? (
          <div className="space-y-2">
            {Array.from({ length: 5 }).map((_, i) => (
              <Skeleton key={i} variant="row" />
            ))}
          </div>
        ) : items.length === 0 ? (
          <EmptyState
            eyebrow={hasFilters ? "Activity · no matches" : "Activity · log idle"}
            message={
              hasFilters
                ? "No activity matches the current filters."
                : "Activity will appear here after the first parse."
            }
          />
        ) : (
          <ActivityFeed
            items={items}
            emptyMessage="No activity recorded yet."
            onItemClick={(evt) => {
              // SP21 Phase 4 Task 4.7: drill into the right surface
              // based on event kind. `claim_*` and `provider_added`
              // navigate away via `eventKindToUrl` (the Dashboard uses
              // the same helper). `remit_received` events stay on
              // `/activity` and open the RemitDrawer via `open(id)`
              // — `eventKindToUrl` still returns `null` for that kind
              // because cross-page navigation isn't the right UX here
              // (we want to keep the activity feed as context behind
              // the drawer). Anything else falls back to the
              // "coming soon" toast so the click still gives feedback.
              const url = eventKindToUrl(evt);
              if (url) navigate(url);
              else if (evt.kind === "remit_received" && evt.remittanceId) {
                open(evt.remittanceId);
              } else {
                toast.info(
                  `Drill for ${evt.kind.replace(/_/g, " ")} coming in a later phase.`,
                );
              }
            }}
          />
        )}
      </div>

      {/* SP21 Phase 4 Task 4.7: RemitDrawer mount. The activity
          feed's `remit_received` rows drill into the drawer via
          `open()`. `remits` is empty (the activity feed doesn't
          keep a flat list of remits around), so j/k is a no-op
          here — closing reverts the URL via `close()`. */}
      <RemitDrawer
        remitId={remitId}
        remits={[]}
        onClose={close}
        onNavigate={open}
        onToggleHelp={() => {
          // ActivityLog has no cheatsheet; `?` is a no-op here.
        }}
      />
    </div>
  );
}
