import { createDm } from '@chat-app/shared/chat';
import {
  acceptFriendRequest,
  type Friendship,
  type ProfileBrief,
  removeFriendship,
  searchProfiles,
  sendFriendRequest,
} from '@chat-app/shared/friends';
import { extractErrorCode } from '@chat-app/shared/i18n';
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useNavigate } from 'react-router-dom';

import { Avatar } from '../components/Avatar';
import { EmptyState } from '../components/EmptyState';
import { NicknameDialog } from '../components/NicknameDialog';
import {
  AddUserIcon,
  AlertIcon,
  ChatBubbleIcon,
  CheckCircleIcon,
  PlusIcon,
  SearchIcon,
  SpinnerIcon,
  UsersIcon,
} from '../components/icons';
import { useFriendshipsContext } from '../context/FriendshipsContext';
import { supabase } from '../lib/supabase';

type Tab = 'friends' | 'pending' | 'requests';

export function FriendsPage() {
  const { t } = useTranslation(['app', 'errors']);
  const { friendships, loading, error, refresh } = useFriendshipsContext();
  const navigate = useNavigate();
  const [tab, setTab] = useState<Tab>('friends');
  const [query, setQuery] = useState('');
  const [debounced, setDebounced] = useState('');
  const [results, setResults] = useState<ProfileBrief[]>([]);
  const [searching, setSearching] = useState(false);
  const [searchError, setSearchError] = useState<string | null>(null);
  const [pendingId, setPendingId] = useState<string | null>(null);
  const [actionError, setActionError] = useState<string | null>(null);
  const [nicknameDialog, setNicknameDialog] = useState<{ userId: string; displayName: string } | null>(null);
  const searchInputRef = useRef<HTMLInputElement | null>(null);

  const focusSearch = useCallback(() => {
    searchInputRef.current?.focus();
  }, []);

  useEffect(() => {
    const id = window.setTimeout(() => setDebounced(query.trim()), 250);
    return () => window.clearTimeout(id);
  }, [query]);

  useEffect(() => {
    if (debounced.length < 2) {
      setResults([]);
      setSearchError(null);
      return;
    }
    let cancelled = false;
    setSearching(true);
    searchProfiles(supabase, debounced)
      .then((data) => {
        if (!cancelled) {
          setResults(data);
          setSearchError(null);
        }
      })
      .catch((err: unknown) => {
        if (!cancelled) setSearchError(translateError(err, t));
      })
      .finally(() => {
        if (!cancelled) setSearching(false);
      });
    return () => {
      cancelled = true;
    };
  }, [debounced, t]);

  const friendsByPeerId = useMemo(() => {
    const map = new Map<string, Friendship>();
    for (const f of friendships) map.set(f.peer.userId, f);
    return map;
  }, [friendships]);

  const accepted = useMemo(
    () => friendships.filter((f) => f.status === 'accepted'),
    [friendships],
  );
  const outgoing = useMemo(
    () => friendships.filter((f) => f.status === 'pending' && f.direction === 'outgoing'),
    [friendships],
  );
  const incoming = useMemo(
    () => friendships.filter((f) => f.status === 'pending' && f.direction === 'incoming'),
    [friendships],
  );

  const performAction = useCallback(
    async (id: string, fn: () => Promise<void>) => {
      setPendingId(id);
      setActionError(null);
      try {
        await fn();
        await refresh();
      } catch (err: unknown) {
        setActionError(translateError(err, t));
      } finally {
        setPendingId(null);
      }
    },
    [refresh, t],
  );

  return (
    <div className="min-h-full bg-surface-3 text-fg">
      <div className="mx-auto flex min-h-full max-w-3xl flex-col gap-5 px-6 py-8">
      <header className="flex items-center justify-between gap-4">
        <h1 className="font-display text-2xl font-semibold tracking-tight text-fg">
          {t('app:friends.title')}
        </h1>
      </header>

      <div className="space-y-3">
        <div className="relative">
          <SearchIcon className="pointer-events-none absolute left-3 top-1/2 h-4 w-4 -translate-y-1/2 text-fg-muted" />
          <input
            ref={searchInputRef}
            type="search"
            value={query}
            onChange={(e) => setQuery(e.target.value)}
            placeholder={t('app:friends.search_placeholder')}
            autoComplete="off"
            autoCapitalize="none"
            spellCheck={false}
            className="w-full rounded-lg border border-line bg-surface-2 py-2.5 pl-10 pr-3 text-sm text-fg placeholder-fg-muted transition focus:border-accent focus:outline-none focus:ring-2 focus:ring-accent/30"
          />
        </div>
        {query.length > 0 && (
          <SearchResults
            query={debounced}
            searching={searching}
            results={results}
            error={searchError}
            friendsByPeerId={friendsByPeerId}
            pendingId={pendingId}
            onAction={performAction}
          />
        )}
      </div>

      <div className="flex gap-2 border-b border-line">
        <TabButton active={tab === 'friends'} onClick={() => setTab('friends')}>
          {t('app:friends.tab_friends')} · {accepted.length}
        </TabButton>
        <TabButton active={tab === 'pending'} onClick={() => setTab('pending')}>
          {t('app:friends.tab_pending')} · {outgoing.length}
        </TabButton>
        <TabButton active={tab === 'requests'} onClick={() => setTab('requests')}>
          {t('app:friends.tab_requests')} · {incoming.length}
        </TabButton>
      </div>

      {error && <Banner kind="error">{error}</Banner>}
      {actionError && <Banner kind="error">{actionError}</Banner>}

      {loading ? (
        <LoadingRow />
      ) : tab === 'friends' ? (
        accepted.length === 0 ? (
          <EmptyState
            icon={<AddUserIcon className="h-8 w-8" />}
            title={t('app:friends.empty_title', { defaultValue: 'Noch keine Freunde' })}
            description={t('app:friends.empty_desc', {
              defaultValue: 'Suche einen Friend per Username oder schicke eine Einladung.',
            })}
            action={{
              label: t('app:friends.empty_cta', { defaultValue: 'Friend hinzufügen' }),
              onClick: focusSearch,
            }}
          />
        ) : (
          <FriendList
            items={accepted}
            emptyKey="app:friends.empty_friends"
            onRowContextMenu={(f) =>
              setNicknameDialog({
                userId: f.peer.userId,
                displayName: f.peer.displayName ?? f.peer.username ?? 'Freund',
              })
            }
            renderActions={(f) => (
              <FriendActions
                busy={pendingId === f.peer.userId || pendingId === 'msg-' + f.peer.userId}
                onMessage={() =>
                  performAction('msg-' + f.peer.userId, async () => {
                    const id = await createDm(supabase, f.peer.userId);
                    navigate('/chats/' + id);
                  })
                }
                onUnfriend={() =>
                  performAction(f.peer.userId, () => removeFriendship(supabase, f.peer.userId))
                }
              />
            )}
          />
        )
      ) : tab === 'pending' ? (
        <FriendList
          items={outgoing}
          emptyKey="app:friends.empty_pending"
          renderActions={(f) => (
            <SecondaryButton
              busy={pendingId === f.peer.userId}
              onClick={() =>
                performAction(f.peer.userId, () => removeFriendship(supabase, f.peer.userId))
              }
            >
              {t('app:friends.action_cancel')}
            </SecondaryButton>
          )}
        />
      ) : (
        <FriendList
          items={incoming}
          emptyKey="app:friends.empty_requests"
          renderActions={(f) => (
            <RequestActions
              busy={pendingId === f.peer.userId}
              onAccept={() =>
                performAction(f.peer.userId, () => acceptFriendRequest(supabase, f.peer.userId))
              }
              onDecline={() =>
                performAction(f.peer.userId, () => removeFriendship(supabase, f.peer.userId))
              }
            />
          )}
        />
      )}
      </div>
      <NicknameDialog
        open={nicknameDialog !== null}
        userId={nicknameDialog?.userId ?? ''}
        displayName={nicknameDialog?.displayName ?? ''}
        onClose={() => setNicknameDialog(null)}
      />
    </div>
  );
}

function translateError(err: unknown, t: ReturnType<typeof useTranslation>['t']): string {
  const code = extractErrorCode(err);
  if (code) return t('errors:' + code, { defaultValue: t('errors:generic') });
  if (err instanceof Error) return err.message;
  return t('errors:generic');
}

// ---------------------------------------------------------------------------

function TabButton({
  active,
  onClick,
  children,
}: {
  active: boolean;
  onClick: () => void;
  children: React.ReactNode;
}) {
  return (
    <button
      type="button"
      onClick={onClick}
      className={
        '-mb-px cursor-pointer border-b-2 px-3 pb-2.5 text-sm font-medium transition focus:outline-none ' +
        (active
          ? 'border-accent text-fg'
          : 'border-transparent text-fg-muted hover:text-fg')
      }
    >
      {children}
    </button>
  );
}

function FriendList({
  items,
  emptyKey,
  renderActions,
  onRowContextMenu,
}: {
  items: Friendship[];
  emptyKey: string;
  renderActions: (f: Friendship) => React.ReactNode;
  onRowContextMenu?: (f: Friendship) => void;
}) {
  const { t } = useTranslation(['app']);
  if (items.length === 0) {
    return (
      <div className="flex flex-1 flex-col items-center justify-center text-center text-sm text-fg-muted">
        <div className="mb-3 flex h-12 w-12 items-center justify-center rounded-2xl border border-line bg-surface-2 text-fg-muted">
          <UsersIcon className="h-5 w-5" />
        </div>

        <p>{t(emptyKey)}</p>
      </div>
    );
  }
  return (
    <ul className="space-y-1.5">
      {items.map((f) => (
        <li key={f.peer.userId}>
          <FriendRow
            profile={f.peer}
            {...(onRowContextMenu
              ? {
                  onContextMenu: (e: React.MouseEvent<HTMLDivElement>) => {
                    e.preventDefault();
                    onRowContextMenu(f);
                  },
                }
              : {})}
          >
            {renderActions(f)}
          </FriendRow>
        </li>
      ))}
    </ul>
  );
}

function FriendRow({
  profile,
  children,
  onContextMenu,
}: {
  profile: ProfileBrief;
  children: React.ReactNode;
  onContextMenu?: (e: React.MouseEvent<HTMLDivElement>) => void;
}) {
  return (
    <div
      onContextMenu={onContextMenu}
      className="flex items-center gap-3 rounded-xl border border-line bg-surface-2 px-4 py-3"
    >
      <Avatar
        url={profile.avatarUrl}
        displayName={profile.displayName ?? profile.username}
        className="h-9 w-9 text-sm"
      />
      <div className="min-w-0 flex-1">
        <p className="truncate text-sm font-semibold text-fg">{profile.displayName}</p>
        <p className="truncate text-xs text-fg-muted">@{profile.username}</p>
      </div>
      <div className="flex shrink-0 gap-2">{children}</div>
    </div>
  );
}

function FriendActions({
  busy,
  onMessage,
  onUnfriend,
}: {
  busy: boolean;
  onMessage: () => void;
  onUnfriend: () => void;
}) {
  const { t } = useTranslation(['app']);
  return (
    <>
      <PrimaryButton
        busy={busy}
        onClick={onMessage}
        icon={<ChatBubbleIcon className="h-3.5 w-3.5" />}
      >
        {t('app:friends.action_message')}
      </PrimaryButton>
      <DangerButton busy={busy} onClick={onUnfriend}>
        {t('app:friends.action_unfriend')}
      </DangerButton>
    </>
  );
}

function RequestActions({
  busy,
  onAccept,
  onDecline,
}: {
  busy: boolean;
  onAccept: () => void;
  onDecline: () => void;
}) {
  const { t } = useTranslation(['app']);
  return (
    <>
      <PrimaryButton
        busy={busy}
        onClick={onAccept}
        icon={<CheckCircleIcon className="h-3.5 w-3.5" />}
      >
        {t('app:friends.action_accept')}
      </PrimaryButton>
      <SecondaryButton busy={busy} onClick={onDecline}>
        {t('app:friends.action_decline')}
      </SecondaryButton>
    </>
  );
}

function PrimaryButton({
  busy,
  onClick,
  icon,
  children,
}: {
  busy: boolean;
  onClick: () => void;
  icon?: React.ReactNode;
  children: React.ReactNode;
}) {
  return (
    <button
      type="button"
      disabled={busy}
      onClick={onClick}
      className="inline-flex cursor-pointer items-center gap-1.5 rounded-md bg-accent px-3 py-1.5 text-xs font-semibold text-accent-fg transition hover:brightness-110 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/40 disabled:cursor-not-allowed disabled:opacity-60"
    >
      {busy ? <SpinnerIcon className="h-3.5 w-3.5" /> : icon}
      <span>{children}</span>
    </button>
  );
}

function SecondaryButton({
  busy,
  onClick,
  children,
}: {
  busy: boolean;
  onClick: () => void;
  children: React.ReactNode;
}) {
  return (
    <button
      type="button"
      disabled={busy}
      onClick={onClick}
      className="inline-flex cursor-pointer items-center gap-1.5 rounded-md border border-line bg-surface-3 px-3 py-1.5 text-xs font-medium text-fg transition hover:brightness-95 focus:outline-none focus-visible:ring-2 focus-visible:ring-accent/30 disabled:cursor-not-allowed disabled:opacity-60"
    >
      {busy && <SpinnerIcon className="h-3.5 w-3.5" />}
      <span>{children}</span>
    </button>
  );
}

function DangerButton({
  busy,
  onClick,
  children,
}: {
  busy: boolean;
  onClick: () => void;
  children: React.ReactNode;
}) {
  return (
    <button
      type="button"
      disabled={busy}
      onClick={onClick}
      className="inline-flex cursor-pointer items-center gap-1.5 rounded-md border border-rose-500/40 bg-rose-500/10 px-3 py-1.5 text-xs font-medium text-rose-600 transition hover:bg-rose-500/20 focus:outline-none focus-visible:ring-2 focus-visible:ring-rose-400/40 disabled:cursor-not-allowed disabled:opacity-60 dark:text-rose-300"
    >
      {busy && <SpinnerIcon className="h-3.5 w-3.5" />}
      <span>{children}</span>
    </button>
  );
}

function LoadingRow() {
  return (
    <div className="flex items-center gap-3 text-sm text-fg-muted">
      <SpinnerIcon className="h-4 w-4 text-accent" />
      <span>…</span>
    </div>
  );
}

function Banner({ kind, children }: { kind: 'error' | 'info'; children: React.ReactNode }) {
  const isError = kind === 'error';
  return (
    <div
      role={isError ? 'alert' : 'status'}
      className={
        'flex items-start gap-3 rounded-lg border p-3 text-sm ' +
        (isError
          ? 'border-rose-500/30 bg-rose-500/10 text-rose-700 dark:text-rose-100'
          : 'border-emerald-500/30 bg-emerald-500/10 text-emerald-700 dark:text-emerald-100')
      }
    >
      {isError ? (
        <AlertIcon className="mt-0.5 h-5 w-5 shrink-0 text-rose-500 dark:text-rose-400" />
      ) : (
        <CheckCircleIcon className="mt-0.5 h-5 w-5 shrink-0 text-emerald-600 dark:text-emerald-400" />
      )}
      <p className="min-w-0 flex-1 break-words">{children}</p>
    </div>
  );
}

function SearchResults({
  query,
  searching,
  results,
  error,
  friendsByPeerId,
  pendingId,
  onAction,
}: {
  query: string;
  searching: boolean;
  results: ProfileBrief[];
  error: string | null;
  friendsByPeerId: Map<string, Friendship>;
  pendingId: string | null;
  onAction: (id: string, fn: () => Promise<void>) => Promise<void>;
}) {
  const { t } = useTranslation(['app']);

  if (query.length < 2) {
    return <p className="text-xs text-fg-muted">{t('app:friends.search_min_chars')}</p>;
  }
  if (searching) {
    return (
      <div className="flex items-center gap-2 text-xs text-fg-muted">
        <SpinnerIcon className="h-3.5 w-3.5 text-accent" />
        <span>…</span>
      </div>
    );
  }
  if (error) return <Banner kind="error">{error}</Banner>;
  if (results.length === 0) {
    return <p className="text-xs text-fg-muted">{t('app:friends.search_no_results')}</p>;
  }

  return (
    <div>
      <p className="mb-2 text-xs font-medium uppercase tracking-[0.1em] text-fg-muted">
        {t('app:friends.search_results_title')}
      </p>
      <ul className="space-y-1.5">
        {results.map((p) => {
          const existing = friendsByPeerId.get(p.userId);
          return (
            <li key={p.userId}>
              <FriendRow profile={p}>
                <SearchActionButton
                  profile={p}
                  existing={existing}
                  busy={pendingId === p.userId}
                  onAction={onAction}
                />
              </FriendRow>
            </li>
          );
        })}
      </ul>
    </div>
  );
}

function SearchActionButton({
  profile,
  existing,
  busy,
  onAction,
}: {
  profile: ProfileBrief;
  existing: Friendship | undefined;
  busy: boolean;
  onAction: (id: string, fn: () => Promise<void>) => Promise<void>;
}) {
  const { t } = useTranslation(['app']);

  if (existing?.status === 'accepted') {
    return (
      <span className="inline-flex items-center gap-1.5 rounded-md border border-emerald-500/30 bg-emerald-500/10 px-3 py-1.5 text-xs font-medium text-emerald-700 dark:text-emerald-200">
        <CheckCircleIcon className="h-3.5 w-3.5" />
        {t('app:friends.already_friends')}
      </span>
    );
  }

  if (existing?.status === 'pending' && existing.direction === 'outgoing') {
    return (
      <span className="inline-flex items-center gap-1.5 rounded-md border border-line bg-surface-2 px-3 py-1.5 text-xs text-fg-muted">
        {t('app:friends.request_sent')}
      </span>
    );
  }

  if (existing?.status === 'pending' && existing.direction === 'incoming') {
    return (
      <PrimaryButton
        busy={busy}
        onClick={() =>
          void onAction(profile.userId, () => acceptFriendRequest(supabase, profile.userId))
        }
        icon={<CheckCircleIcon className="h-3.5 w-3.5" />}
      >
        {t('app:friends.action_accept')}
      </PrimaryButton>
    );
  }

  return (
    <PrimaryButton
      busy={busy}
      onClick={() =>
        void onAction(profile.userId, () => sendFriendRequest(supabase, profile.userId))
      }
      icon={<PlusIcon className="h-3.5 w-3.5" />}
    >
      {t('app:friends.send_request')}
    </PrimaryButton>
  );
}
