import type * as React from "react";
import {
  BellOff,
  ChevronDown,
  CircleDot,
  FileText,
  Hash,
  Lock,
  X,
} from "lucide-react";

import {
  ContextMenu,
  ContextMenuContent,
  ContextMenuTrigger,
} from "@/shared/ui/context-menu";

import { ChannelContextMenuItems } from "@/features/sidebar/ui/ChannelContextMenu";
import type { ActiveChannelTurnSummary } from "@/features/agents/activeAgentTurnsStore";
import { formatElapsed } from "@/features/agents/ui/agentSessionUtils";
import { getEphemeralChannelDisplay } from "@/features/channels/lib/ephemeralChannel";
import { EphemeralChannelBadge } from "@/features/channels/ui/EphemeralChannelBadge";
import {
  DEFAULT_HOVER_PROFILE_STATUS_GEOMETRY,
  ProfileAvatarWithStatus,
  scaleProfileAvatarStatusGeometry,
} from "@/features/profile/ui/ProfileAvatarWithStatus";
import type { Channel, PresenceStatus } from "@/shared/api/types";
import { cn } from "@/shared/lib/cn";
import { useNow } from "@/shared/lib/useNow";
import {
  SidebarGroup,
  SidebarGroupContent,
  SidebarGroupLabel,
  SidebarMenu,
  SidebarMenuButton,
  SidebarMenuItem,
} from "@/shared/ui/sidebar";
import { ChannelActivityPopover } from "@/features/sidebar/ui/ChannelActivityPopover";
import { useAppShell } from "@/app/AppShellContext";

const SECTION_LABEL_BUTTON_CLASS =
  "group/section-label flex w-fit max-w-[calc(100%-3rem)] cursor-pointer appearance-none items-center gap-1 text-left transition-colors hover:text-sidebar-foreground focus-visible:text-sidebar-foreground";
const SECTION_LABEL_CHEVRON_CLASS =
  "relative size-2.5 shrink-0 text-current opacity-0 transition-[color,opacity] group-hover/sidebar-section:opacity-100 group-hover/section-label:opacity-100 group-focus-within/sidebar-section:opacity-100 group-focus-visible/section-label:opacity-100 group-data-[section-actions-open=true]/sidebar-section:opacity-100";
const SECTION_LABEL_CHEVRON_ICON_CLASS =
  "absolute left-1/2 top-1/2 size-2.5 -translate-x-1/2 -translate-y-1/2";
const SIDEBAR_ROW_ACTION_VISIBILITY_CLASS =
  "group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 md:opacity-0";
const SIDEBAR_ROW_ACTION_REPLACED_BADGE_CLASS =
  "max-md:opacity-0 md:group-focus-within/menu-item:opacity-0 md:group-hover/menu-item:opacity-0";
const SIDEBAR_ROW_ICON_ACTION_CLASS =
  "flex size-6 items-center justify-center p-1 text-sidebar-foreground/45 transition-colors hover:text-sidebar-foreground focus-visible:text-sidebar-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-sidebar-ring peer-data-[active=true]/menu-button:text-sidebar-active-foreground/75 peer-data-[active=true]/menu-button:hover:text-sidebar-active-foreground [&>svg]:size-4 [&>svg]:shrink-0";
const DM_AVATAR_SIZE = 24;
const DM_AVATAR_STATUS_GEOMETRY = scaleProfileAvatarStatusGeometry(
  DEFAULT_HOVER_PROFILE_STATUS_GEOMETRY,
  DM_AVATAR_SIZE,
);

function formatUnreadCount(count: number): string {
  return count > 99 ? "99+" : String(count);
}

function UnreadCountBadge({
  channelName,
  className,
  count,
}: {
  channelName: string;
  className?: string;
  count: number;
}) {
  return (
    <span
      className={cn(
        "flex h-5 min-w-5 shrink-0 items-center justify-center rounded-full bg-primary px-1 text-2xs font-semibold leading-none text-primary-foreground tabular-nums",
        className,
      )}
      data-testid={`channel-unread-${channelName}`}
    >
      {formatUnreadCount(count)}
      <span className="sr-only"> new comment{count === 1 ? "" : "s"}</span>
    </span>
  );
}

function UnreadDotBadge({
  channelName,
  className,
}: {
  channelName: string;
  className?: string;
}) {
  return (
    <span
      className={cn("h-2 w-2 shrink-0 rounded-full bg-primary", className)}
      data-testid={`channel-unread-dot-${channelName}`}
    >
      <span className="sr-only">unread</span>
    </span>
  );
}

function formatAgentCount(count: number) {
  return `${count} ${count === 1 ? "agent" : "agents"}`;
}

export function formatWorkingTooltip(
  summary: ActiveChannelTurnSummary,
): string {
  const leadName = summary.agentNames?.[0];

  if (!leadName) {
    return `${formatAgentCount(summary.agentCount)} working`;
  }

  const remainingAgentCount = summary.agentCount - 1;
  if (remainingAgentCount <= 0) {
    return `${leadName} working`;
  }

  return `${leadName} and ${formatAgentCount(remainingAgentCount)} working`;
}

function ChannelWorkingBadge({
  channelName,
  isActive,
  summary,
}: {
  channelName: string;
  isActive: boolean;
  summary: ActiveChannelTurnSummary;
}) {
  const now = useNow(1000);
  const elapsed = formatElapsed(now - summary.anchorAt);
  const label =
    summary.agentCount > 1 ? `${elapsed} (${summary.agentCount})` : elapsed;
  const title = formatWorkingTooltip(summary);

  return (
    <span
      className={cn(
        "max-w-32 shrink-0 truncate rounded-full px-1.5 py-0.5 text-2xs font-medium leading-none tabular-nums motion-safe:animate-pulse group-data-[collapsible=icon]:hidden",
        "hidden sm:inline-flex",
        isActive
          ? "bg-sidebar-active-foreground/20 text-sidebar-active-foreground"
          : "bg-primary/10 text-primary",
      )}
      data-testid={`channel-working-${channelName}`}
      title={title}
    >
      {label}
    </span>
  );
}

export type SidebarDmParticipant = {
  avatarUrl: string | null;
  label: string;
  pubkey: string;
};

function DmChannelIcon({
  channelName,
  isPair,
  participants,
  presenceStatus,
}: {
  channelName: string;
  isPair: boolean;
  participants?: SidebarDmParticipant[];
  presenceStatus?: PresenceStatus;
}) {
  const primaryParticipant = participants?.[0];

  if (!primaryParticipant) {
    return <CircleDot className="h-4 w-4" />;
  }

  if (!isPair && participants && participants.length > 1) {
    return (
      <span
        aria-hidden="true"
        className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-sidebar-accent/80 text-2xs font-semibold leading-none text-sidebar-foreground shadow-none"
        data-testid={`channel-dm-count-${channelName}`}
      >
        <span className="translate-x-px leading-none">
          {participants.length}
        </span>
      </span>
    );
  }

  if (isPair || !participants || participants.length <= 1) {
    return (
      <span className="relative flex h-6 w-6 shrink-0 items-center justify-center">
        <ProfileAvatarWithStatus
          avatarClassName="bg-sidebar-accent/80 text-2xs text-sidebar-foreground shadow-none"
          avatarUrl={primaryParticipant.avatarUrl}
          className="h-6 w-6"
          geometry={DM_AVATAR_STATUS_GEOMETRY}
          iconClassName="h-3.5 w-3.5"
          label={primaryParticipant.label}
          size={DM_AVATAR_SIZE}
          status={presenceStatus}
          statusTestId={`channel-presence-${channelName}`}
        />
      </span>
    );
  }

  return <CircleDot className="h-4 w-4" />;
}

function SidebarChannelIcon({
  channel,
  className,
  dmParticipants,
  presenceStatus,
}: {
  channel: Channel;
  className?: string;
  dmParticipants?: SidebarDmParticipant[];
  presenceStatus?: PresenceStatus;
}) {
  if (channel.channelType === "dm") {
    return (
      <DmChannelIcon
        channelName={channel.name}
        isPair={channel.participantPubkeys.length === 2}
        participants={dmParticipants}
        presenceStatus={
          dmParticipants?.length === 1 ||
          channel.participantPubkeys.length === 2
            ? presenceStatus
            : undefined
        }
      />
    );
  }

  if (channel.visibility === "private") {
    return <Lock className={cn("h-4 w-4", className)} />;
  }

  if (channel.channelType === "forum") {
    return <FileText className={cn("h-4 w-4", className)} />;
  }

  return <Hash className={cn("h-4 w-4", className)} />;
}

export function ChannelMenuButton({
  channel,
  label,
  isActive,
  hasUnread,
  activeWorking,
  isMuted,
  dmParticipants,
  presenceStatus,
  onSelectChannel,
}: {
  channel: Channel;
  label?: string;
  isActive: boolean;
  hasUnread: boolean;
  unreadCount?: number;
  activeWorking?: ActiveChannelTurnSummary;
  isMuted?: boolean;
  dmParticipants?: SidebarDmParticipant[];
  presenceStatus?: PresenceStatus;
  onSelectChannel: (channelId: string) => void;
}) {
  const resolvedLabel = label ?? channel.name;
  const ephemeralDisplay = getEphemeralChannelDisplay(channel);
  const {
    hasSidebarUnreadProjections,
    topLevelUnreadChannelIds,
    unreadThreadChannelIds,
  } = useAppShell();
  const hasTopLevelUnread =
    channel.channelType === "dm"
      ? hasUnread
      : hasSidebarUnreadProjections
        ? topLevelUnreadChannelIds.has(channel.id)
        : hasUnread;
  const hasThreadUnread =
    channel.channelType !== "dm" &&
    (hasSidebarUnreadProjections
      ? unreadThreadChannelIds.has(channel.id)
      : hasUnread);
  const inactiveContentOpacity = cn(
    !isActive && !hasTopLevelUnread && !isMuted && "opacity-80",
    !isActive &&
      isMuted &&
      !hasTopLevelUnread &&
      !hasThreadUnread &&
      "sidebar-muted-content opacity-50 dark:opacity-45",
  );

  const button = (
    <SidebarMenuButton
      className={cn(
        "data-[active=true]:font-normal",
        isActive
          ? "group-hover/menu-item:bg-sidebar-active group-hover/menu-item:text-sidebar-active-foreground"
          : "group-hover/menu-item:bg-sidebar-accent group-hover/menu-item:text-sidebar-foreground",
        hasTopLevelUnread &&
          "font-bold text-sidebar-foreground hover:text-sidebar-foreground data-[active=true]:font-bold",
      )}
      data-channel-id={channel.id}
      data-testid={`channel-${channel.name}`}
      isActive={isActive}
      onClick={() => onSelectChannel(channel.id)}
      tooltip={resolvedLabel}
      type="button"
    >
      <SidebarChannelIcon
        channel={channel}
        className={
          channel.channelType === "dm" ? undefined : inactiveContentOpacity
        }
        dmParticipants={dmParticipants}
        presenceStatus={presenceStatus}
      />
      <span
        className={cn("min-w-0 flex-1 truncate", inactiveContentOpacity)}
        data-sidebar-row-label
      >
        {resolvedLabel}
      </span>
      {ephemeralDisplay ? (
        <EphemeralChannelBadge
          display={ephemeralDisplay}
          testId={`channel-ephemeral-${channel.name}`}
          variant="sidebar"
        />
      ) : null}
      {activeWorking ? (
        <ChannelWorkingBadge
          channelName={channel.name}
          isActive={isActive}
          summary={activeWorking}
        />
      ) : null}
      {isMuted ? (
        <BellOff
          className={cn(
            "ml-auto h-4 w-4 shrink-0",
            isActive
              ? "text-sidebar-active-foreground/60"
              : "text-sidebar-foreground/40",
          )}
        />
      ) : null}
      {hasThreadUnread ? (
        <UnreadDotBadge channelName={channel.name} className="ml-auto" />
      ) : null}
    </SidebarMenuButton>
  );

  if (!activeWorking && !hasThreadUnread) {
    return button;
  }

  return (
    <ChannelActivityPopover activeWorking={activeWorking} channel={channel}>
      {button}
    </ChannelActivityPopover>
  );
}

export function SidebarSection({
  action,
  activeWorkingByChannelId,
  dmParticipantsByChannelId,
  emptyState,
  items,
  channelLabels,
  isCollapsed,
  isActiveChannel,
  presenceByChannelId,
  selectedChannelId,
  title,
  testId,
  unreadChannelCounts,
  unreadChannelIds,
  onHideDm,
  onMarkChannelRead,
  onMarkChannelUnread,
  onSelectChannel,
  onToggleCollapsed,
  mutedChannelIds,
  onMuteChannel,
  onUnmuteChannel,
  sectionActionsOpen,
}: {
  action?: React.ReactNode;
  activeWorkingByChannelId?: ReadonlyMap<string, ActiveChannelTurnSummary>;
  dmParticipantsByChannelId?: Record<string, SidebarDmParticipant[]>;
  emptyState?: React.ReactNode;
  items: Channel[];
  channelLabels?: Record<string, string>;
  isCollapsed?: boolean;
  isActiveChannel: boolean;
  presenceByChannelId?: Record<string, PresenceStatus>;
  selectedChannelId: string | null;
  title: string;
  testId: string;
  unreadChannelCounts: ReadonlyMap<string, number>;
  unreadChannelIds: ReadonlySet<string>;
  onHideDm?: (channelId: string) => void;
  onMarkChannelRead?: (
    channelId: string,
    lastMessageAt: string | null | undefined,
  ) => void;
  onMarkChannelUnread?: (channelId: string) => void;
  onSelectChannel: (channelId: string) => void;
  onToggleCollapsed?: () => void;
  mutedChannelIds?: ReadonlySet<string>;
  onMuteChannel?: (channelId: string) => void;
  onUnmuteChannel?: (channelId: string) => void;
  sectionActionsOpen?: boolean;
}) {
  if (items.length === 0 && !action && !emptyState) {
    return null;
  }

  const contentId = `sidebar-${testId}`;
  const canToggle = Boolean(onToggleCollapsed);

  return (
    <SidebarGroup
      className="group/sidebar-section select-none"
      data-section-actions-open={sectionActionsOpen || undefined}
    >
      <div className="relative">
        <SidebarGroupLabel asChild={canToggle}>
          {canToggle ? (
            <button
              aria-controls={contentId}
              aria-expanded={!isCollapsed}
              className={SECTION_LABEL_BUTTON_CLASS}
              data-testid={`${testId}-section-label`}
              onClick={onToggleCollapsed}
              type="button"
            >
              <span data-sidebar-section-title>{title}</span>
              <span aria-hidden="true" className={SECTION_LABEL_CHEVRON_CLASS}>
                <ChevronDown
                  className={cn(
                    SECTION_LABEL_CHEVRON_ICON_CLASS,
                    isCollapsed ? "-rotate-90" : "rotate-0",
                  )}
                />
              </span>
            </button>
          ) : (
            title
          )}
        </SidebarGroupLabel>
        {action}
      </div>
      {!isCollapsed ? (
        <SidebarGroupContent id={contentId}>
          {items.length > 0 ? (
            <SidebarMenu data-testid={testId}>
              {items.map((channel) => {
                const menuItem = (
                  <SidebarMenuItem
                    key={onMarkChannelUnread ? undefined : channel.id}
                    className="group/menu-item"
                  >
                    <ChannelMenuButton
                      channel={channel}
                      activeWorking={activeWorkingByChannelId?.get(channel.id)}
                      dmParticipants={dmParticipantsByChannelId?.[channel.id]}
                      hasUnread={unreadChannelIds.has(channel.id)}
                      unreadCount={unreadChannelCounts.get(channel.id) ?? 0}
                      isMuted={mutedChannelIds?.has(channel.id)}
                      isActive={
                        isActiveChannel && selectedChannelId === channel.id
                      }
                      label={channelLabels?.[channel.id] ?? channel.name}
                      presenceStatus={presenceByChannelId?.[channel.id]}
                      onSelectChannel={onSelectChannel}
                    />
                    {channel.channelType === "dm" &&
                    unreadChannelIds.has(channel.id) &&
                    !(isActiveChannel && selectedChannelId === channel.id) ? (
                      <UnreadCountBadge
                        channelName={channel.name}
                        className={cn(
                          "pointer-events-none absolute right-1 top-1/2 -translate-y-1/2 transition-opacity",
                          onHideDm && SIDEBAR_ROW_ACTION_REPLACED_BADGE_CLASS,
                        )}
                        count={Math.max(
                          unreadChannelCounts.get(channel.id) ?? 0,
                          1,
                        )}
                      />
                    ) : null}
                    {channel.channelType === "dm" && onHideDm ? (
                      <button
                        aria-label="Close direct message"
                        className={cn(
                          "absolute right-1 top-1/2 z-10 -translate-y-1/2 after:absolute after:-inset-2 after:md:hidden group-data-[collapsible=icon]:hidden",
                          SIDEBAR_ROW_ICON_ACTION_CLASS,
                          SIDEBAR_ROW_ACTION_VISIBILITY_CLASS,
                        )}
                        data-sidebar="menu-action"
                        data-testid={`hide-dm-${channel.name}`}
                        onClick={(event) => {
                          event.stopPropagation();
                          onHideDm(channel.id);
                        }}
                        type="button"
                      >
                        <X />
                      </button>
                    ) : null}
                  </SidebarMenuItem>
                );

                // The shared menu always renders copy actions, so every row
                // gets a context menu regardless of read/mute availability.
                return (
                  <ContextMenu key={channel.id}>
                    <ContextMenuTrigger asChild>{menuItem}</ContextMenuTrigger>
                    <ContextMenuContent>
                      <ChannelContextMenuItems
                        channel={channel}
                        hasUnread={unreadChannelIds.has(channel.id)}
                        isMuted={mutedChannelIds?.has(channel.id)}
                        onMarkChannelRead={onMarkChannelRead}
                        onMarkChannelUnread={onMarkChannelUnread}
                        onMuteChannel={onMuteChannel}
                        onUnmuteChannel={onUnmuteChannel}
                      />
                    </ContextMenuContent>
                  </ContextMenu>
                );
              })}
            </SidebarMenu>
          ) : emptyState ? (
            <div
              className="px-2 py-1 text-sm text-sidebar-foreground/60"
              data-testid={`${testId}-empty`}
            >
              {emptyState}
            </div>
          ) : null}
        </SidebarGroupContent>
      ) : null}
    </SidebarGroup>
  );
}
