'use client';

import { memo, useCallback, useMemo, useState } from 'react';
import type { RoomWithCategory } from '@/contexts/room-context';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { cn } from '@/lib/utils';
import { ChevronDown, ChevronRight, Hash, Lock, Plus, X, GripVertical, BellOff, Archive } from 'lucide-react';
import {
  DndContext,
  closestCorners,
  KeyboardSensor,
  PointerSensor,
  useSensor,
  useSensors,
  useDroppable,
  type DragEndEvent,
  type UniqueIdentifier,
} from '@dnd-kit/core';
import {
  SortableContext,
  sortableKeyboardCoordinates,
  verticalListSortingStrategy,
  useSortable,
} from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';

/* ── Types ─────────────────────────────────────────────────────── */

interface RoomWithUnread extends RoomWithCategory {
  unread_count?: number;
  muted?: boolean;
  archived?: boolean;
}

interface DiscordChannelSidebarProps {
  projectName: string;
  rooms: RoomWithCategory[];
  selectedRoomId: string | null;
  onSelectRoom: (room: RoomWithCategory) => void;
  onCreateRoom: () => void;
  categories: Array<{ id: string; name: string }>;
  onCreateCategory: (name: string) => Promise<void>;
  onMoveRoomToCategory: (roomId: string, categoryId: string | null) => void;
  onMuteRoom?: (roomId: string, muted: boolean) => void;
  onArchiveRoom?: (roomId: string, archived: boolean) => void;
  onOpenSettings?: () => void;
}

type CatName = string;

const DRAG_PREFIX = 'room:';
const CAT_PREFIX = 'cat:';

/* ── Draggable row ─────────────────────────────────────────────── */

const DraggableRow = memo(function DraggableRow({
  roomId,
  children,
}: {
  roomId: string;
  children: React.ReactNode;
}) {
  const {
    attributes,
    listeners,
    setNodeRef,
    transform,
    transition,
    isDragging,
  } = useSortable({ id: `${DRAG_PREFIX}${roomId}` });

  const style: React.CSSProperties = {
    transform: CSS.Transform.toString(transform),
    transition,
  };

  return (
    <li
      ref={setNodeRef}
      style={style}
      {...attributes}
      {...listeners}
      className={cn(
        'group cursor-grab active:cursor-grabbing',
        isDragging && 'opacity-40',
      )}
    >
      {children}
    </li>
  );
});

/* ── Single room button ────────────────────────────────────────── */

const RoomButton = memo(function RoomButton({
  room,
  selectedRoomId,
  onSelectRoom,
  onMute,
  onArchive,
}: {
  room: RoomWithCategory;
  selectedRoomId: string | null;
  onSelectRoom: (room: RoomWithCategory) => void;
  onMute?: (roomId: string, muted: boolean) => void;
  onArchive?: (roomId: string, archived: boolean) => void;
}) {
  const isSelected = selectedRoomId === room.id;
  const meta = room as RoomWithUnread;
  const unreadCount = meta.unread_count ?? 0;
  const muted = meta.muted ?? false;
  const archived = meta.archived ?? false;

  return (
    <button
      type="button"
      onClick={() => onSelectRoom(room)}
      className={cn('discord-channel-item w-full group', isSelected && 'active', archived && 'opacity-50')}
    >
      <GripVertical className="h-3.5 w-3.5 text-muted-foreground opacity-0 group-hover:opacity-70 shrink-0 mr-1" />
      <Hash className="discord-channel-hash" />
      <span className="discord-channel-name">{room.room_name}</span>
      {!room.public && !muted && !archived && (
        <Lock className="h-3.5 w-3.5 opacity-50 shrink-0" />
      )}
      {muted && <BellOff className="h-3 w-3 opacity-50 shrink-0 text-amber-500" aria-label="Muted" />}
      {archived && <Archive className="h-3 w-3 opacity-50 shrink-0 text-muted-foreground" aria-label="Archived" />}
      {!muted && !archived && unreadCount > 0 && (
        <span className="discord-mention-badge">
          {unreadCount > 99 ? '99+' : unreadCount}
        </span>
      )}

      {/* Mute / Archive context actions */}
      {(onMute || onArchive) && (
        <div className="ml-auto hidden group-hover:flex items-center gap-0.5">
          {onMute && (
            <button
              type="button"
              onClick={(e) => { e.stopPropagation(); onMute(room.id, !muted); }}
              className="p-0.5 rounded hover:bg-muted cursor-pointer bg-transparent border-0"
              title={muted ? 'Unmute' : 'Mute'}
            >
              <BellOff className={cn('h-3 w-3', muted ? 'text-amber-500' : 'text-muted-foreground')} />
            </button>
          )}
          {onArchive && (
            <button
              type="button"
              onClick={(e) => { e.stopPropagation(); onArchive(room.id, !archived); }}
              className="p-0.5 rounded hover:bg-muted cursor-pointer bg-transparent border-0"
              title={archived ? 'Unarchive' : 'Archive'}
            >
              <Archive className={cn('h-3 w-3', archived ? 'text-foreground' : 'text-muted-foreground')} />
            </button>
          )}
        </div>
      )}
    </button>
  );
});

/* ── Category group ────────────────────────────────────────────── */

const ChannelGroup = memo(function ChannelGroup({
  categoryId,
  categoryName,
  rooms,
  selectedRoomId,
  onSelectRoom,
  isCollapsed,
  onToggle,
  canReceiveDrops,
  onMute,
  onArchive,
}: {
  categoryId: string;
  categoryName: string;
  rooms: RoomWithCategory[];
  selectedRoomId: string | null;
  onSelectRoom: (room: RoomWithCategory) => void;
  isCollapsed?: boolean;
  onToggle?: () => void;
  canReceiveDrops?: true;
  onMute?: (roomId: string, muted: boolean) => void;
  onArchive?: (roomId: string, archived: boolean) => void;
}) {
  const ids: UniqueIdentifier[] = rooms.map((r) => `${DRAG_PREFIX}${r.id}`);

  // Category header is sortable (for drag-to-reorder categories)
  const {
    attributes: catAttrs,
    listeners: catListeners,
    setNodeRef: setCatRef,
    transform: catTransform,
    transition: catTransition,
    isDragging: isCatDragging,
  } = useSortable({ id: `${CAT_PREFIX}${categoryId}` });

  const catStyle: React.CSSProperties = {
    transform: CSS.Transform.toString(catTransform),
    transition: catTransition,
    opacity: isCatDragging ? 0.5 : 1,
  };

  // Make the category header a droppable zone so rooms can be dragged onto it
  const { setNodeRef: setHeaderRef, isOver: isOverHeader } = useDroppable({ id: categoryName });

  // Aggregate unread from all rooms in this category
  const totalUnread = rooms.reduce((acc, r) => acc + ((r as RoomWithUnread).unread_count ?? 0), 0);

  return (
    <div
      className="discord-channel-category"
      onDragOver={(e) => e.preventDefault()}
      onDrop={canReceiveDrops ? () => undefined /* handled by DnD */ : undefined}
    >
      <div ref={setCatRef} style={catStyle} className="flex items-center">
        <button
          ref={setHeaderRef}
          {...catAttrs}
          {...catListeners}
          className={cn(
            'discord-channel-category-header w-full flex-1',
            isCollapsed && 'collapsed',
            isOverHeader && 'ring-1 ring-accent',
          )}
          onClick={onToggle}
          title={isCollapsed ? 'Expand' : 'Collapse'}
        >
          {isCollapsed ? (
            <ChevronRight className="h-3 w-3 shrink-0" />
          ) : (
            <ChevronDown className="h-3 w-3 shrink-0" />
          )}
          <GripVertical className="h-3 w-3 text-muted-foreground opacity-0 group-hover:opacity-70 shrink-0" />
          <span className="flex-1 text-left">{categoryName}</span>
          {totalUnread > 0 && (
            <span className="text-[10px] px-1 rounded-full bg-muted text-muted-foreground">
              {totalUnread > 99 ? '99+' : totalUnread}
            </span>
          )}
        </button>
      </div>

      {!isCollapsed && (
        <ul className="space-y-0.5 pl-2">
          <SortableContext items={ids} strategy={verticalListSortingStrategy}>
            {rooms.map((room) => (
              <DraggableRow key={room.id} roomId={room.id}>
                <RoomButton
                  room={room}
                  selectedRoomId={selectedRoomId}
                  onSelectRoom={onSelectRoom}
                  onMute={onMute}
                  onArchive={onArchive}
                />
              </DraggableRow>
            ))}
          </SortableContext>
        </ul>
      )}
    </div>
  );
});

/* ── List content ──────────────────────────────────────────────── */

function ChannelListContent({
  rooms,
  selectedRoomId,
  onSelectRoom,
  categories,
  uncategorizedRooms,
  categorizedRooms,
  collapsedState,
  toggleCategory,
  onMoveRoom,
  onMute,
  onArchive,
}: {
  rooms: RoomWithCategory[];
  selectedRoomId: string | null;
  onSelectRoom: (room: RoomWithCategory) => void;
  categories: Array<{ id: string; name: string }>;
  uncategorizedRooms: RoomWithCategory[];
  categorizedRooms: Map<CatName, RoomWithCategory[]>;
  collapsedState: Record<string, boolean>;
  toggleCategory: (name: string) => void;
  onMoveRoom: (roomId: string, catId: string | null) => void;
  onMute?: (roomId: string, muted: boolean) => void;
  onArchive?: (roomId: string, archived: boolean) => void;
}) {
  const sensors = useSensors(
    useSensor(PointerSensor, { activationConstraint: { distance: 4 } }),
    useSensor(KeyboardSensor, { coordinateGetter: sortableKeyboardCoordinates }),
  );

  // Build flat list of all category names in display order for collision targets
  const sortedCatNames = useMemo(
    () => Array.from(categorizedRooms.keys()).sort(),
    [categorizedRooms],
  );

  const handleDragEnd = useCallback(
    (event: DragEndEvent) => {
      const { over } = event;
      if (!over) return;

      const dragId = String(event.active.id);

      // Category reorder
      if (dragId.startsWith(CAT_PREFIX)) {
        // Category reordering — emit an event if the parent component manages order
        // Currently categories don't have a backend order API; handled by parent
        return;
      }

      if (!dragId.startsWith(DRAG_PREFIX)) return;

      const draggedRoomId = dragId.slice(DRAG_PREFIX.length);

      // Dropping onto a category (target id matches a category name)
      const targetName = String(over.id);
      const targetCat = categories.find((c) => c.name === targetName);
      if (targetCat) {
        const currentRoom = rooms.find((r) => r.id === draggedRoomId);
        if (currentRoom && currentRoom.category_info?.id !== targetCat.id) {
          onMoveRoom(draggedRoomId, targetCat.id);
        }
        return;
      }

      // Dropping onto another room's sort position → nothing to change visually
      // dnd-kit handles the reorder via SortableContext automatically
    },
    [categories, rooms, onMoveRoom],
  );

  return (
    <DndContext
      sensors={sensors}
      collisionDetection={closestCorners}
      onDragEnd={handleDragEnd}
    >
      {/* Uncategorized channels at top */}
      {uncategorizedRooms.length > 0 && (
        <ChannelGroup
          categoryId="__uncategorized__"
          categoryName="Channels"
          rooms={uncategorizedRooms}
          selectedRoomId={selectedRoomId}
          onSelectRoom={onSelectRoom}
          onMute={onMute}
          onArchive={onArchive}
        />
      )}

      {/* Categorized groups */}
      {sortedCatNames.map((catName) => {
        const cat = categories.find((c) => c.name === catName);
        return (
          <ChannelGroup
            key={catName}
            categoryId={cat?.id ?? catName}
            categoryName={catName}
            rooms={categorizedRooms.get(catName)!}
            selectedRoomId={selectedRoomId}
            onSelectRoom={onSelectRoom}
            isCollapsed={!!collapsedState[catName]}
            onToggle={() => toggleCategory(catName)}
            canReceiveDrops
            onMute={onMute}
            onArchive={onArchive}
          />
        );
      })}
    </DndContext>
  );
}

/* ── Public component ──────────────────────────────────────────── */

export const DiscordChannelSidebar = memo(function DiscordChannelSidebar({
  projectName,
  rooms,
  selectedRoomId,
  onSelectRoom,
  onCreateRoom,
  categories,
  onCreateCategory,
  onMoveRoomToCategory,
  onMuteRoom,
  onArchiveRoom,
  onOpenSettings,
}: DiscordChannelSidebarProps) {
  const [collapsed, setCollapsed] = useState<Record<string, boolean>>({});
  const toggleCategory = useCallback((name: string) => {
    setCollapsed((prev) => ({ ...prev, [name]: !prev[name] }));
  }, []);

  const [creatingCat, setCreatingCat] = useState(false);
  const [newCatName, setNewCatName] = useState('');

  const handleCreateCategory = useCallback(async () => {
    const trimmed = newCatName.trim();
    if (!trimmed) return;
    await onCreateCategory(trimmed);
    setNewCatName('');
    setCreatingCat(false);
  }, [newCatName, onCreateCategory]);

  const handleMoveRoom = useCallback(
    (roomId: string, categoryId: string | null) => {
      onMoveRoomToCategory(roomId, categoryId);
    },
    [onMoveRoomToCategory],
  );

  // Group rooms by category — empty categories still show as collapsible groups
  const uncategorized = useMemo(
    () => rooms.filter((r) => !r.category),
    [rooms],
  );
  const categorized = useMemo(
    () => rooms.filter((r) => r.category),
    [rooms],
  );

  const categoryMap = useMemo(() => {
    // Start with ALL categories (including empty ones), then merge in rooms
    const map = new Map<CatName, RoomWithCategory[]>();
    for (const cat of categories) {
      if (!map.has(cat.name)) map.set(cat.name, []);
    }
    for (const room of categorized) {
      const catName = room.category_info?.name;
      if (catName && map.has(catName)) {
        map.get(catName)!.push(room);
      }
    }
    return map;
  }, [categorized, categories]);

  return (
    <div className="discord-channel-sidebar flex flex-col h-full">
      {/* Header */}
      <div className="discord-channel-header flex items-center justify-between px-3 py-2 border-b">
        <div
          className="flex items-center gap-2 font-bold truncate"
          style={{ color: 'var(--room-text)' }}
        >
          <span className="truncate">{projectName}</span>
        </div>
        <div className="flex items-center gap-1">
          {onOpenSettings && (
            <Button
              variant="ghost"
              size="icon"
              className="h-7 w-7"
              style={{ color: 'var(--room-text-muted)' }}
              title="Channel settings"
              onClick={onOpenSettings}
            >
              <svg className="h-4 w-4" viewBox="0 0 24 24" fill="currentColor">
                <path d="M12 15a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z" />
                <path fillRule="evenodd" clipRule="evenodd" d="M1.786 9.476 1.12 7.236l2.14-.665a6.013 6.013 0 0 1 1.634-2.824l-1.29-1.872 2.142-1.237 1.676 1.478a6.013 6.013 0 0 1 3.07 0l1.677-1.478 2.14 1.237-1.288 1.872a6.013 6.013 0 0 1 1.634 2.824l2.14.665-1.12 2.827-2.142.664a6.013 6.013 0 0 1 0 3.088l2.142.664 1.12 2.827-2.14.665a6.013 6.013 0 0 1-1.634 2.824l1.29 1.872-2.142 1.237-1.677-1.478a6.013 6.013 0 0 1-3.07 0l-1.676 1.478-2.142-1.237 1.288-1.872a6.013 6.013 0 0 1-1.634-2.824l-2.14-.665 1.12-2.827 2.142-.664a6.013 6.013 0 0 1 0-3.088l-2.142-.664ZM12 16a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z" />
              </svg>
            </Button>
          )}
        </div>
      </div>

      {/* Scrollable list */}
      <div className="discord-channel-list flex-1 overflow-y-auto px-2 py-2">
        <ChannelListContent
          rooms={rooms}
          selectedRoomId={selectedRoomId}
          onSelectRoom={onSelectRoom}
          categories={categories}
          uncategorizedRooms={uncategorized}
          categorizedRooms={categoryMap}
          collapsedState={collapsed}
          toggleCategory={toggleCategory}
          onMoveRoom={handleMoveRoom}
          onMute={onMuteRoom}
          onArchive={onArchiveRoom}
        />

        {rooms.length === 0 && (
          <div className="px-4 py-8 text-center">
            <p className="text-sm mb-3 text-muted-foreground">No channels yet</p>
            <Button
              size="sm"
              onClick={onCreateRoom}
              className="bg-primary hover:bg-primary/90 text-primary-foreground border-none"
            >
              <Plus className="mr-1 h-3.5 w-3.5" />
              Create Channel
            </Button>
          </div>
        )}

        {/* Add Category — always at the bottom */}
        {creatingCat ? (
          <div className="flex items-center gap-1 px-2 py-1">
            <Input
              autoFocus
              value={newCatName}
              onChange={(e) => setNewCatName(e.target.value)}
              onKeyDown={(e) => {
                if (e.key === 'Enter') handleCreateCategory();
                if (e.key === 'Escape') { setCreatingCat(false); setNewCatName(''); }
              }}
              placeholder="Category name"
              className="h-6 text-[10px]"
            />
            <Button size="icon" variant="ghost" className="h-6 w-6" onClick={handleCreateCategory}>
              <Plus className="h-3 w-3" />
            </Button>
            <Button size="icon" variant="ghost" className="h-6 w-6" onClick={() => { setCreatingCat(false); setNewCatName(''); }}>
              <X className="h-2.5 w-2.5" />
            </Button>
          </div>
        ) : (
          <button
            onClick={() => setCreatingCat(true)}
            className="discord-channel-item w-full px-2 py-1 text-[10px] text-muted-foreground hover:text-foreground"
          >
            + Add Category
          </button>
        )}
      </div>

      {/* Footer */}
      <div
        className="border-t px-2 py-2"
        style={{ borderColor: 'var(--room-border)', background: 'var(--room-sidebar)' }}
      >
        <button
          onClick={onCreateRoom}
          className="discord-add-channel-btn w-full flex items-center gap-2 text-sm"
        >
          <Plus className="h-4 w-4" />
          <span>Add Channel</span>
        </button>
      </div>
    </div>
  );
});
