import { useEffect, useMemo, useState } from 'react';
import { useServerStore } from '../stores/server.ts';
import { useChannelStore } from '../stores/channel.ts';
import { VoiceChannel } from './VoiceChannel.tsx';
import { CreateChannelModal } from './CreateChannelModal.tsx';
import { InviteModal } from './InviteModal.tsx';
import { ChannelSettingsModal } from './ChannelSettingsModal.tsx';
import { useNotificationSettingsStore } from '../stores/notificationSettings.ts';
import { useReadStatesStore } from '../stores/readStates.ts';
import { useContextMenu } from './ContextMenu.tsx';
import { api } from '../lib/api.ts';

type NotifLevel = 'all' | 'mentions' | 'none';

const NOTIF_LABELS: Record<NotifLevel, string> = {
  all: '🔔 All',
  mentions: '🔔 @',
  none: '🔕 Muted',
};

const NOTIF_CYCLE: NotifLevel[] = ['all', 'mentions', 'none'];

interface ServerGroup {
  id: string;
  server_id: string;
  name: string;
  position: number;
}

function collapsedKey(serverId: string, groupId: string) {
  return 'collapsed:' + serverId + ':' + groupId;
}

function isCollapsed(serverId: string, groupId: string): boolean {
  try {
    return localStorage.getItem(collapsedKey(serverId, groupId)) === '1';
  } catch {
    return false;
  }
}

function toggleCollapsed(serverId: string, groupId: string) {
  const key = collapsedKey(serverId, groupId);
  const now = isCollapsed(serverId, groupId);
  try {
    if (now) {
      localStorage.removeItem(key);
    } else {
      localStorage.setItem(key, '1');
    }
  } catch {
    // ignore
  }
}

export function ChannelList() {
  const activeServerId = useServerStore((state) => state.activeServerId);
  const servers = useServerStore((state) => state.servers);
  const channelsByServer = useChannelStore((state) => state.channelsByServer);
  const fetchChannels = useChannelStore((state) => state.fetchChannels);
  const activeChannelId = useChannelStore((state) => state.activeChannelId);
  const setActiveChannel = useChannelStore((state) => state.setActiveChannel);
  const removeChannel = useChannelStore((state) => state.removeChannel);
  const updateChannel = useChannelStore((state) => state.updateChannel);
  const [showCreate, setShowCreate] = useState(false);
  const [createInGroup, setCreateInGroup] = useState<string | null>(null);
  const [showInvite, setShowInvite] = useState(false);
  const [settingsChannel, setSettingsChannel] = useState<{ id: string; name: string } | null>(null);
  const [groups, setGroups] = useState<ServerGroup[]>([]);
  const [toggleTick, setToggleTick] = useState(0);

  // Inline editing state
  const [editingChannelId, setEditingChannelId] = useState<string | null>(null);
  const [editingChannelName, setEditingChannelName] = useState('');
  const [editingGroupId, setEditingGroupId] = useState<string | null>(null);
  const [editingGroupName, setEditingGroupName] = useState('');

  const notifSettings = useNotificationSettingsStore((state) => state.settings);
  const fetchNotifSettings = useNotificationSettingsStore((state) => state.fetchSettings);
  const setNotifLevel = useNotificationSettingsStore((state) => state.setLevel);

  const readStates = useReadStatesStore((state) => state.states);
  const fetchReadStates = useReadStatesStore((state) => state.fetchStates);

  const { showMenu, MenuPortal } = useContextMenu();

  const refreshGroups = () => {
    if (activeServerId) {
      api.get<ServerGroup[]>('/servers/' + activeServerId + '/groups')
        .then(setGroups)
        .catch(() => setGroups([]));
    }
  };

  useEffect(() => {
    if (activeServerId) {
      fetchChannels(activeServerId);
      refreshGroups();
    } else {
      setGroups([]);
    }
  }, [activeServerId, fetchChannels]);

  useEffect(() => {
    fetchNotifSettings();
    fetchReadStates();
  }, [fetchNotifSettings, fetchReadStates]);

  const channels = useMemo(() => {
    return activeServerId ? channelsByServer[activeServerId] || [] : [];
  }, [activeServerId, channelsByServer]);

  const activeServer = useMemo(() => {
    if (!activeServerId) return null;
    return servers.find((s) => s.id === activeServerId) || null;
  }, [servers, activeServerId]);

  // Build ordered sections: sorted groups first, then ungroupped channels by category
  const sections = useMemo(() => {
    type Section = { type: 'group'; group: ServerGroup; channels: typeof channels } | { type: 'category'; name: string; channels: typeof channels };

    const grouped: Record<string, typeof channels> = {};
    const ungrouped: Record<string, typeof channels> = {};

    for (const channel of channels) {
      if (channel.group_id) {
        const list = grouped[channel.group_id] || [];
        list.push(channel);
        grouped[channel.group_id] = list;
      } else {
        const cat = channel.category || 'TEXT CHANNELS';
        const list = ungrouped[cat] || [];
        list.push(channel);
        ungrouped[cat] = list;
      }
    }

    const result: Section[] = [];

    // Sorted groups
    const sortedGroups = [...groups].sort((a, b) => a.position - b.position);
    for (const grp of sortedGroups) {
      const chs = (grouped[grp.id] || []).sort((a, b) => a.position - b.position);
      result.push({ type: 'group', group: grp, channels: chs });
    }

    // Ungroupped channels by category
    const sortedCats = Object.keys(ungrouped).sort();
    for (const cat of sortedCats) {
      result.push({ type: 'category', name: cat, channels: ungrouped[cat].sort((a, b) => a.position - b.position) });
    }

    return result;
  }, [channels, groups, toggleTick]);

  const getLevel = (channelId: string): NotifLevel => {
    return notifSettings[channelId] || 'all';
  };

  const handleNotifClick = (e: React.MouseEvent, channelId: string) => {
    e.stopPropagation();
    const current = getLevel(channelId);
    const idx = NOTIF_CYCLE.indexOf(current);
    const next = NOTIF_CYCLE[(idx + 1) % NOTIF_CYCLE.length];
    setNotifLevel(channelId, next);
  };

  const notifIcon = (channelId: string) => {
    const level = getLevel(channelId);
    if (level === 'none') return '🔕';
    return level === 'mentions' ? '🔔@' : '🔔';
  };

  const hasUnread = (channelId: string): boolean => {
    return !(channelId in readStates);
  };

  // --- Channel context menu ---
  const handleChannelContextMenu = (e: React.MouseEvent, channel: { id: string; name: string }) => {
    showMenu(e, [
      { label: 'Edit Name', icon: '✏️', onClick: () => startEditChannel(channel) },
      { label: 'Permissions', icon: '🔒', onClick: () => setSettingsChannel({ id: channel.id, name: channel.name }) },
      { label: 'Delete Channel', icon: '🗑️', danger: true, onClick: () => deleteChannel(channel.id, channel.name) },
    ]);
  };

  const startEditChannel = (channel: { id: string; name: string }) => {
    setEditingChannelId(channel.id);
    setEditingChannelName(channel.name);
  };

  const commitEditChannel = async () => {
    const id = editingChannelId;
    const name = editingChannelName.trim();
    setEditingChannelId(null);
    if (!id || !name) return;
    try {
      const updated = await api.patch<{ id: string; server_id: string; name: string; type: string; category: string | null; position: number; group_id?: string | null }>(
        `/servers/${activeServerId}/channels/${id}`,
        { name }
      );
      updateChannel(updated as any);
    } catch (err) {
      console.error('Rename channel failed:', err);
    }
  };

  const deleteChannel = async (channelId: string, channelName: string) => {
    if (!confirm(`Delete #${channelName}? This cannot be undone.`)) return;
    try {
      await api.delete(`/servers/${activeServerId}/channels/${channelId}`);
      removeChannel(channelId);
    } catch { /* ignore */ }
  };

  // --- Group context menu ---
  const handleGroupContextMenu = (e: React.MouseEvent, group: ServerGroup) => {
    showMenu(e, [
      { label: 'Edit Name', icon: '✏️', onClick: () => startEditGroup(group) },
      { label: 'Create Channel Here', icon: '➕', onClick: () => { setCreateInGroup(group.id); setShowCreate(true); } },
      { label: 'Delete Group', icon: '🗑️', danger: true, onClick: () => deleteGroup(group.id, group.name) },
    ]);
  };

  const startEditGroup = (group: ServerGroup) => {
    setEditingGroupId(group.id);
    setEditingGroupName(group.name);
  };

  const commitEditGroup = async () => {
    const id = editingGroupId;
    const name = editingGroupName.trim();
    setEditingGroupId(null);
    if (!id || !name) return;
    try {
      await api.patch(`/servers/${activeServerId}/groups/${id}`, { name });
      refreshGroups();
    } catch (err) {
      console.error('Rename group failed:', err);
    }
  };

  const deleteGroup = async (groupId: string, groupName: string) => {
    if (!confirm(`Delete group "${groupName}"? Channels will be moved to ungrouped.`)) return;
    try {
      await api.delete(`/servers/${activeServerId}/groups/${groupId}`);
      refreshGroups();
      if (activeServerId) fetchChannels(activeServerId);
    } catch { /* ignore */ }
  };

  // --- Channel row renderer ---
  const renderChannel = (channel: typeof channels[0]) => {
    if (channel.type === 'voice') {
      return (
        <VoiceChannel key={channel.id} channelId={channel.id} channelName={channel.name} />
      );
    }

    const isEditing = editingChannelId === channel.id;

    return (
      <div key={channel.id} className="group" onContextMenu={(e) => handleChannelContextMenu(e, channel)}>
        {isEditing ? (
          <div className="w-full text-left px-2 py-1 rounded-sm flex items-center gap-2">
            <span className="text-gb-fg-f">#</span>
            <input
              autoFocus
              value={editingChannelName}
              onChange={(e) => setEditingChannelName(e.target.value)}
              onBlur={commitEditChannel}
              onKeyDown={(e) => { if (e.key === 'Enter') commitEditChannel(); if (e.key === 'Escape') setEditingChannelId(null); }}
              className="flex-1 bg-gb-bg-t text-gb-fg px-1 py-0.5 text-sm outline-none border border-gb-orange"
            />
          </div>
        ) : (
          <button
            onClick={() => setActiveChannel(channel.id)}
            className={`w-full text-left px-2 py-1 rounded-sm flex items-center gap-2 ${
              channel.id === activeChannelId ? 'terminal-active' : 'hover:bg-gb-bg-t text-gb-fg-s'
            }`}
          >
            <span className="text-gb-fg-f">
              {channel.type === 'forum' ? '■' : channel.type === 'calendar' ? '○' : channel.type === 'docs' ? '☰' : channel.type === 'list' ? '☑' : '#'}
            </span>
            <span className="truncate flex-1">{channel.name}</span>
            {hasUnread(channel.id) && (
              <span className="text-gb-orange text-xs font-bold" title="Unread messages">●</span>
            )}
            <span className="flex items-center gap-1 text-xs pointer-events-none group-hover:pointer-events-auto">
              <span
                onClick={(e) => handleNotifClick(e, channel.id)}
                className="text-gb-fg-f hover:text-gb-orange opacity-0 group-hover:opacity-100 transition-opacity cursor-pointer"
                title={`Notifications: ${NOTIF_LABELS[getLevel(channel.id)]}`}
              >
                {notifIcon(channel.id)}
              </span>
              <span
                onClick={(e) => { e.stopPropagation(); setSettingsChannel({ id: channel.id, name: channel.name }); }}
                className="text-gb-fg-f hover:text-gb-orange opacity-0 group-hover:opacity-100 cursor-pointer"
                title="Channel settings"
              >
                [⚙]
              </span>
            </span>
          </button>
        )}
      </div>
    );
  };

  return (
    <div className="h-full w-56 bg-gb-bg-s border-r border-gb-bg-t flex flex-col">
      <div className="terminal-border border-t-0 border-x-0 px-3 py-2 text-gb-fg truncate flex items-center justify-between">
        <span>{activeServerId ? `[SERVER ${activeServer?.name ?? activeServerId}]` : '[NO SERVER]'}</span>
        {activeServerId && (
          <div className="flex gap-1">
            <button
              onClick={() => setShowInvite(true)}
              className="terminal-button text-xs"
              title="Invite people"
            >
              [INV]
            </button>
            <button
              onClick={() => { setCreateInGroup(null); setShowCreate(true); }}
              className="terminal-button text-xs"
              title="Create channel"
            >
              [+]
            </button>
          </div>
        )}
      </div>
      <div className="flex-1 overflow-y-auto p-2 font-mono text-sm">
        {sections.length === 0 && (
          <p className="text-gb-fg-f">[no channels]</p>
        )}
        {sections.map((section) => {
          if (section.type === 'group') {
            const grp = section.group;
            const collapsed = isCollapsed(activeServerId || '', grp.id);
            const isEditingGroup = editingGroupId === grp.id;
            return (
              <div key={'grp:' + grp.id} className="mb-3">
                {isEditingGroup ? (
                  <div className="text-gb-fg-t text-xs uppercase mb-1 flex items-center">
                    <span className="mr-1">{collapsed ? '[+]' : '[-]'}</span>
                    <input
                      autoFocus
                      value={editingGroupName}
                      onChange={(e) => setEditingGroupName(e.target.value)}
                      onBlur={commitEditGroup}
                      onKeyDown={(e) => { if (e.key === 'Enter') commitEditGroup(); if (e.key === 'Escape') setEditingGroupId(null); }}
                      className="flex-1 bg-gb-bg-t text-gb-fg px-1 py-0.5 text-xs outline-none border border-gb-orange"
                    />
                  </div>
                ) : (
                  <div
                    className="text-gb-fg-t text-xs uppercase mb-1 cursor-pointer select-none hover:text-gb-fg flex items-center"
                    onClick={() => { toggleCollapsed(activeServerId || '', grp.id); setToggleTick(t => t + 1); }}
                    onContextMenu={(e) => handleGroupContextMenu(e, grp)}
                  >
                    <span className="mr-1">{collapsed ? '[+]' : '[-]'}</span>
                    <span>{grp.name}</span>
                  </div>
                )}
                {!collapsed && (
                  <>
                    <div className="text-gb-fg-f text-xs mb-1">---</div>
                    {section.channels.map((channel) => renderChannel(channel))}
                  </>
                )}
              </div>
            );
          }
          // category section (fallback for ungrouped channels)
          return (
            <div key={'cat:' + section.name} className="mb-3">
              <div className="text-gb-fg-t text-xs uppercase mb-1">{section.name}</div>
              <div className="text-gb-fg-f text-xs mb-1">---</div>
              {section.channels.map((channel) => renderChannel(channel))}
            </div>
          );
        })}
      </div>
      {showCreate && activeServerId && (
        <CreateChannelModal
          serverId={activeServerId}
          defaultGroupId={createInGroup}
          onClose={() => { setShowCreate(false); setCreateInGroup(null); }}
        />
      )}
      {showInvite && activeServerId && activeServer && (
        <InviteModal serverId={activeServerId} serverName={activeServer.name} onClose={() => setShowInvite(false)} />
      )}
      {settingsChannel && activeServerId && (
        <ChannelSettingsModal
          serverId={activeServerId}
          channelId={settingsChannel.id}
          channelName={settingsChannel.name}
          onClose={() => setSettingsChannel(null)}
        />
      )}
      {MenuPortal}
    </div>
  );
}
