import { useCallback, useEffect, useRef, useState } from 'react';
import type { ReactElement, SyntheticEvent } from 'react';
import type { Conversation, Message, MediaRef, PublicUser, User } from '@altricade/core';
import { heartbeat } from '@altricade/core/api';
import { SessionProvider, useSession } from '../entities/session';
import { AuthForm } from '../features/auth';
import { RealtimeProvider } from '../features/realtime';
import { useConversations, ConversationSidebar } from '../features/conversations';
import { useFolders } from '../features/folders';
import { ContactsPanel } from '../features/contacts';
import { ChatView } from '../features/messaging';
import { ProfilePanel } from '../features/profile';
import { SettingsPanel } from '../features/settings';
import { NotificationsProvider, useNotifications, unregisterWebPush } from '../features/notifications';
import { apiConfig } from '../shared/api';
import {
  PlusIcon,
  CloseIcon,
  UsersIcon,
  UserIcon,
  ChatsIcon,
  SettingsIcon,
  ChevronLeftIcon,
} from '../shared/ui';

type RailView = 'chats' | 'contacts' | 'compose' | 'settings';

const RAIL_TITLES: Record<Exclude<RailView, 'chats'>, string> = {
  contacts: 'Contacts',
  compose: 'New group',
  settings: 'Settings',
};

const mediaLabel = (media: MediaRef | null): string => {
  if (media === null) {
    return 'New message';
  }
  switch (media.kind) {
    case 'image':
      return 'Photo';
    case 'video':
      return 'Video';
    case 'video_note':
      return 'Video message';
    case 'voice':
      return 'Voice message';
    default:
      return 'File';
  }
};

const GroupComposer = ({
  onCreate,
}: {
  onCreate: (title: string, members: string[]) => Promise<void>;
}): ReactElement => {
  const [title, setTitle] = useState('');
  const [members, setMembers] = useState('');
  const [error, setError] = useState<string | null>(null);

  const submit = async (event: SyntheticEvent): Promise<void> => {
    event.preventDefault();
    const trimmed = title.trim();
    if (trimmed === '') {
      return;
    }
    const list = members
      .split(',')
      .map((name) => name.trim())
      .filter((name) => name.length > 0);
    setError(null);
    try {
      await onCreate(trimmed, list);
      setTitle('');
      setMembers('');
    } catch {
      setError('Could not create group');
    }
  };

  return (
    <div className="compose-block">
      <p className="rail-section-title">New group</p>
      <form className="stack-form" onSubmit={(event) => void submit(event)}>
        <input
          className="field"
          placeholder="Group name"
          value={title}
          onChange={(event) => {
            setTitle(event.target.value);
          }}
        />
        <input
          className="field"
          placeholder="Members (comma-separated usernames)"
          value={members}
          onChange={(event) => {
            setMembers(event.target.value);
          }}
        />
        <button type="submit" className="btn-primary btn-sm">
          <UsersIcon size={17} />
          Create group
        </button>
      </form>
      {error !== null ? <p className="form-error">{error}</p> : null}
    </div>
  );
};

const Dashboard = ({ user, onLogout }: { user: User; onLogout: () => void }): ReactElement => {
  const me: PublicUser = {
    id: user.id,
    username: user.username,
    displayName: user.displayName,
    avatarUrl: user.avatarUrl,
  };
  const { notify, setOpener } = useNotifications();
  const [current, setCurrent] = useState<Conversation | null>(null);
  const [railView, setRailView] = useState<RailView>('chats');
  const [activeFolderId, setActiveFolderId] = useState<string | null>(null);
  const [profileUser, setProfileUser] = useState<PublicUser | null>(null);
  const convRef = useRef<Conversation[]>([]);
  const folders = useFolders(user.id);

  // Toast (or OS notification when hidden) for messages arriving in other chats.
  const onIncoming = useCallback(
    (message: Message): void => {
      const conversation = convRef.current.find((item) => item.id === message.conversationId);
      const isGroup = conversation?.type === 'group';
      const text = message.content.length > 0 ? message.content : mediaLabel(message.media);
      notify({
        conversationId: message.conversationId,
        title: isGroup ? (conversation.title ?? 'Group') : message.sender.displayName,
        body: isGroup ? `${message.sender.displayName}: ${text}` : text,
      });
    },
    [notify],
  );

  const { conversations, onlineMap, startDirect, createGroupChat, clearChat, deleteChat } =
    useConversations(user.id, current?.id ?? null, onIncoming);
  convRef.current = conversations;

  const openConversation = useCallback((conversation: Conversation): void => {
    setCurrent(conversation);
    setRailView('chats');
    setProfileUser(null);
  }, []);

  // The DM shared with the profiled user, when one exists (drives shared media).
  const profileConversation =
    profileUser === null
      ? null
      : (conversations.find(
          (item) => item.type === 'direct' && item.peer !== null && item.peer.id === profileUser.id,
        ) ?? null);

  // If the open chat disappears from the list (deleted for me, possibly on
  // another device), close it.
  useEffect(() => {
    if (current !== null && !conversations.some((c) => c.id === current.id)) {
      setCurrent(null);
    }
  }, [conversations, current]);

  // If the active folder was deleted (any device), fall back to All.
  useEffect(() => {
    if (activeFolderId !== null && !folders.folders.some((f) => f.id === activeFolderId)) {
      setActiveFolderId(null);
    }
  }, [folders.folders, activeFolderId]);

  // Let notification taps (in-app toast or SW message) open the right chat.
  useEffect(() => {
    setOpener((conversationId) => {
      const conversation = convRef.current.find((item) => item.id === conversationId);
      if (conversation !== undefined) {
        openConversation(conversation);
      }
    });
  }, [setOpener, openConversation]);

  // Deep-link: /?conversation=<id> (from a push opened in a fresh tab).
  useEffect(() => {
    const params = new URLSearchParams(window.location.search);
    const target = params.get('conversation');
    if (target === null || conversations.length === 0) {
      return;
    }
    const conversation = conversations.find((item) => item.id === target);
    if (conversation !== undefined) {
      setCurrent(conversation);
      window.history.replaceState({}, '', window.location.pathname);
    }
  }, [conversations]);

  // Keep-alive heartbeat so our own last-seen stays fresh while connected.
  useEffect(() => {
    const interval = setInterval(() => {
      void heartbeat(apiConfig);
    }, 45000);
    return () => {
      clearInterval(interval);
    };
  }, []);

  const totalUnread = conversations.reduce((sum, item) => sum + item.unreadCount, 0);

  const startDirectAndOpen = async (username: string): Promise<void> => {
    openConversation(await startDirect(username));
  };

  const createGroupAndOpen = async (title: string, members: string[]): Promise<void> => {
    openConversation(await createGroupChat(title, members));
  };

  const shellClass = [
    'app-shell',
    current !== null ? 'has-chat' : '',
    profileUser !== null ? 'has-profile' : '',
  ]
    .filter((token) => token !== '')
    .join(' ');

  return (
    <div className={shellClass}>
      <aside className="rail">
        <div className="rail-header">
          {railView === 'chats' ? (
            <span className="rail-brand">
              <span className="rail-mark" aria-hidden="true">
                A
              </span>
              <span className="rail-title">Altricade</span>
            </span>
          ) : (
            <span className="rail-brand">
              <button
                type="button"
                className="icon-btn"
                aria-label="Back to chats"
                onClick={() => {
                  setRailView('chats');
                }}
              >
                <ChevronLeftIcon />
              </button>
              <span className="rail-title">{RAIL_TITLES[railView]}</span>
            </span>
          )}
          <div className="rail-header-actions">
            {railView === 'chats' ? (
              <>
                <button
                  type="button"
                  className="icon-btn rail-desktop-action"
                  aria-label="Contacts"
                  title="Contacts"
                  onClick={() => {
                    setRailView('contacts');
                  }}
                >
                  <UsersIcon />
                </button>
                <button
                  type="button"
                  className="icon-btn rail-desktop-action"
                  aria-label="Settings"
                  title="Settings"
                  onClick={() => {
                    setRailView('settings');
                  }}
                >
                  <SettingsIcon />
                </button>
                <button
                  type="button"
                  className="icon-btn"
                  aria-label="New group"
                  title="New group"
                  onClick={() => {
                    setRailView('compose');
                  }}
                >
                  <PlusIcon />
                </button>
              </>
            ) : (
              <button
                type="button"
                className="icon-btn"
                aria-label="Close"
                onClick={() => {
                  setRailView('chats');
                }}
              >
                <CloseIcon />
              </button>
            )}
          </div>
        </div>

        {railView === 'chats' ? (
          <ConversationSidebar
            conversations={conversations}
            currentId={current?.id ?? null}
            onlineMap={onlineMap}
            folders={folders}
            activeFolderId={activeFolderId}
            onSelectFolder={setActiveFolderId}
            onSelect={openConversation}
            onStartDirect={startDirectAndOpen}
            onClearChat={clearChat}
            onDeleteChat={deleteChat}
          />
        ) : railView === 'contacts' ? (
          <div className="rail-scroll compose">
            <ContactsPanel onStartDirect={startDirectAndOpen} />
          </div>
        ) : railView === 'compose' ? (
          <div className="rail-scroll compose">
            <GroupComposer onCreate={createGroupAndOpen} />
          </div>
        ) : (
          <div className="rail-scroll">
            <SettingsPanel onLogout={onLogout} />
          </div>
        )}

        {/* Mobile bottom navigation (Telegram-style): Contacts | Chats | Settings */}
        <nav className="rail-nav" aria-label="Main">
          <button
            type="button"
            className="rail-nav-btn"
            aria-pressed={railView === 'contacts'}
            aria-label="Contacts"
            onClick={() => {
              setRailView('contacts');
            }}
          >
            <UserIcon size={24} />
          </button>
          <button
            type="button"
            className="rail-nav-btn"
            aria-pressed={railView === 'chats'}
            aria-label="Chats"
            onClick={() => {
              setRailView('chats');
            }}
          >
            <span className="rail-nav-icon">
              <ChatsIcon size={24} />
              {totalUnread > 0 ? <span className="unread-badge rail-nav-badge">{totalUnread}</span> : null}
            </span>
          </button>
          <button
            type="button"
            className="rail-nav-btn"
            aria-pressed={railView === 'settings'}
            aria-label="Settings"
            onClick={() => {
              setRailView('settings');
            }}
          >
            <SettingsIcon size={24} />
          </button>
        </nav>
      </aside>

      {current !== null ? (
        <ChatView
          conversation={current}
          me={me}
          onlineMap={onlineMap}
          onOpenProfile={(target) => {
            if (target.id !== user.id) {
              setProfileUser(target);
            }
          }}
          onBack={() => {
            setCurrent(null);
          }}
        />
      ) : (
        <section className="chat-pane chat-empty">
          <div className="chat-empty-inner">
            <span className="chat-empty-mark" aria-hidden="true">
              A
            </span>
            <h2>Select a conversation</h2>
            <p className="muted">Search for someone or start a new chat to begin messaging.</p>
          </div>
        </section>
      )}

      {profileUser !== null ? (
        <ProfilePanel
          user={profileUser}
          conversation={profileConversation}
          online={onlineMap[profileUser.id] === true}
          onClose={() => {
            setProfileUser(null);
          }}
          onMessage={(username) => {
            void startDirectAndOpen(username);
          }}
        />
      ) : null}
    </div>
  );
};

const Shell = (): ReactElement => {
  const { status, user, logout } = useSession();

  if (status === 'loading') {
    return (
      <main className="splash">
        <span className="splash-mark" aria-hidden="true">
          A
        </span>
      </main>
    );
  }
  if (user === null) {
    return <AuthForm />;
  }
  return (
    <RealtimeProvider>
      <NotificationsProvider>
        <Dashboard
          user={user}
          onLogout={() => {
            // Detach this browser's push subscription before the token clears.
            void (async () => {
              await unregisterWebPush();
              await logout();
            })();
          }}
        />
      </NotificationsProvider>
    </RealtimeProvider>
  );
};

export const App = (): ReactElement => (
  <SessionProvider>
    <Shell />
  </SessionProvider>
);
