'use client';

import type { DiscordEmojiOption } from '@nexumi/shared';
import { ChevronsUpDown, Search, X } from 'lucide-react';
import { useEffect, useMemo, useRef, useState } from 'react';
import { useTranslations } from '@/components/locale-provider';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { useGuildEmojis } from '@/hooks/use-guild-emojis';
import {
  ALL_UNICODE_EMOJIS,
  pickEmojiNative,
  searchUnicodeEmojis,
  SKIN_TONE_PREVIEW,
  UNICODE_EMOJI_CATEGORIES,
  type UnicodeEmojiCategoryId,
  type UnicodeEmojiItem
} from '@/lib/unicode-emoji-data';
import { cn } from '@/lib/utils';

const CUSTOM_EMOJI_RE = /^<(a)?:([A-Za-z0-9_]+):(\d+)>$/;

export function formatCustomEmoji(emoji: Pick<DiscordEmojiOption, 'id' | 'name' | 'animated'>): string {
  return emoji.animated ? `<a:${emoji.name}:${emoji.id}>` : `<:${emoji.name}:${emoji.id}>`;
}

export function parseCustomEmoji(value: string): { id: string; name: string; animated: boolean } | null {
  const match = value.match(CUSTOM_EMOJI_RE);
  if (!match) {
    return null;
  }
  return {
    animated: Boolean(match[1]),
    name: match[2]!,
    id: match[3]!
  };
}

function customEmojiUrl(id: string, animated: boolean): string {
  return `https://cdn.discordapp.com/emojis/${id}.${animated ? 'gif' : 'png'}?size=64`;
}

function normalizeSearch(value: string): string {
  return value.trim().toLocaleLowerCase();
}

type PickerCategory = 'server' | UnicodeEmojiCategoryId;

interface DiscordEmojiSelectProps {
  guildId: string;
  value?: string;
  onChange: (next: string | undefined) => void;
  disabled?: boolean;
  placeholder?: string;
  allowClear?: boolean;
}

function EmojiPreview({ value, className }: { value: string; className?: string }) {
  const custom = parseCustomEmoji(value);
  if (custom) {
    return (
      // eslint-disable-next-line @next/next/no-img-element -- Discord CDN emoji preview
      <img
        src={customEmojiUrl(custom.id, custom.animated)}
        alt={custom.name}
        className={cn('size-5 shrink-0 object-contain', className)}
      />
    );
  }
  return <span className={cn('text-base leading-none', className)}>{value}</span>;
}

function findUnicodeByNative(native: string): UnicodeEmojiItem | undefined {
  return ALL_UNICODE_EMOJIS.find((emoji) => emoji.skins.includes(native));
}

export function DiscordEmojiSelect({
  guildId,
  value,
  onChange,
  disabled = false,
  placeholder,
  allowClear = true
}: DiscordEmojiSelectProps) {
  const t = useTranslations();
  const { emojis, loading, error, reload } = useGuildEmojis(guildId);
  const [open, setOpen] = useState(false);
  const [query, setQuery] = useState('');
  const [category, setCategory] = useState<PickerCategory>('people');
  const [skinTone, setSkinTone] = useState(0);
  const [skinOpen, setSkinOpen] = useState(false);
  const [hoverLabel, setHoverLabel] = useState<{ native: string; name: string } | null>(null);
  const gridRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (!open) {
      return;
    }
    setHoverLabel(null);
    setSkinOpen(false);
    setCategory(emojis.length > 0 ? 'server' : 'people');
  }, [open, emojis.length]);

  const filteredServer = useMemo(() => {
    const needle = normalizeSearch(query);
    if (!needle) {
      return emojis;
    }
    return emojis.filter((emoji) => {
      const haystack = normalizeSearch(`${emoji.name} ${emoji.id}`);
      return haystack.includes(needle);
    });
  }, [emojis, query]);

  const searchResults = useMemo(() => {
    if (!query.trim()) {
      return [];
    }
    return searchUnicodeEmojis(query, skinTone);
  }, [query, skinTone]);

  const activeUnicodeCategory = useMemo(() => {
    if (category === 'server') {
      return null;
    }
    return UNICODE_EMOJI_CATEGORIES.find((entry) => entry.id === category) ?? null;
  }, [category]);

  const selectedCustom = value ? parseCustomEmoji(value) : null;
  const selectedServer = selectedCustom
    ? emojis.find((emoji) => emoji.id === selectedCustom.id)
    : undefined;
  const selectedUnicode = value && !selectedCustom ? findUnicodeByNative(value) : undefined;

  const footerPreview = hoverLabel ?? (value
    ? {
        native: value,
        name: selectedServer
          ? `:${selectedServer.name}:`
          : selectedCustom
            ? `:${selectedCustom.name}:`
            : selectedUnicode
              ? `:${selectedUnicode.id}:`
              : value
      }
    : null);

  function selectNative(native: string, name: string) {
    onChange(native);
    setHoverLabel({ native, name });
    setOpen(false);
  }

  function selectServerEmoji(emoji: DiscordEmojiOption) {
    const encoded = formatCustomEmoji(emoji);
    onChange(encoded);
    setHoverLabel({ native: encoded, name: `:${emoji.name}:` });
    setOpen(false);
  }

  const isSearching = query.trim().length > 0;
  const categoryButtons: Array<{ id: PickerCategory; icon: string; label: string }> = [
    {
      id: 'server',
      icon: '🏠',
      label: t('modulePages.selfroles.emojiServer')
    },
    ...UNICODE_EMOJI_CATEGORIES.map((entry) => ({
      id: entry.id as PickerCategory,
      icon: entry.icon,
      label: t(`modulePages.selfroles.emojiCategories.${entry.id}`)
    }))
  ];

  return (
    <Popover
      open={open}
      onOpenChange={(next) => {
        setOpen(next);
        if (!next) {
          setQuery('');
          setSkinOpen(false);
        }
      }}
    >
      <PopoverTrigger asChild>
        <Button
          type="button"
          variant="outline"
          role="combobox"
          aria-expanded={open}
          disabled={disabled || loading}
          className="h-9 w-full justify-between px-3 font-normal"
        >
          <span className={cn('flex min-w-0 items-center gap-2', !value && 'text-muted-foreground')}>
            {value ? (
              <>
                <EmojiPreview value={value} />
                <span className="truncate">
                  {selectedServer?.name ?? selectedCustom?.name ?? selectedUnicode?.id ?? value}
                </span>
              </>
            ) : (
              (placeholder ?? t('modulePages.selfroles.emojiPlaceholder'))
            )}
          </span>
          <ChevronsUpDown className="ml-2 size-4 shrink-0 opacity-50" />
        </Button>
      </PopoverTrigger>
      <PopoverContent
        className="w-[22.5rem] overflow-hidden border-border bg-popover p-0 shadow-xl"
        align="start"
        sideOffset={6}
      >
        <div className="flex items-center gap-2 border-b border-border px-3 py-2">
          <div className="relative min-w-0 flex-1">
            <Search className="pointer-events-none absolute top-1/2 left-2.5 size-3.5 -translate-y-1/2 text-muted-foreground" />
            <Input
              value={query}
              onChange={(event) => setQuery(event.target.value)}
              placeholder={t('modulePages.selfroles.emojiSearch')}
              className="h-8 border-0 bg-muted/60 pl-8 shadow-none focus-visible:ring-0"
            />
          </div>
          <div className="relative">
            <Button
              type="button"
              variant="ghost"
              size="icon"
              className="size-8"
              aria-label={t('modulePages.selfroles.emojiSkinTone')}
              onClick={() => setSkinOpen((prev) => !prev)}
            >
              <span className="text-base leading-none">{SKIN_TONE_PREVIEW[skinTone]}</span>
            </Button>
            {skinOpen ? (
              <div className="absolute top-full right-0 z-10 mt-1 flex gap-0.5 rounded-md border border-border bg-popover p-1 shadow-md">
                {SKIN_TONE_PREVIEW.map((preview, index) => (
                  <button
                    key={preview}
                    type="button"
                    className={cn(
                      'flex size-7 items-center justify-center rounded-sm text-base hover:bg-accent',
                      skinTone === index && 'bg-accent'
                    )}
                    onClick={() => {
                      setSkinTone(index);
                      setSkinOpen(false);
                    }}
                  >
                    {preview}
                  </button>
                ))}
              </div>
            ) : null}
          </div>
          {allowClear && value ? (
            <Button
              type="button"
              variant="ghost"
              size="icon"
              className="size-8"
              aria-label={t('common.clearSelection')}
              onClick={() => {
                onChange(undefined);
                setHoverLabel(null);
              }}
            >
              <X className="size-4" />
            </Button>
          ) : null}
        </div>

        <div className="flex h-72">
          {!isSearching ? (
            <div className="flex w-10 shrink-0 flex-col items-center gap-0.5 overflow-y-auto border-r border-border py-2">
              {categoryButtons.map((entry) => (
                <button
                  key={entry.id}
                  type="button"
                  title={entry.label}
                  aria-label={entry.label}
                  className={cn(
                    'flex size-8 items-center justify-center rounded-md text-base transition-colors hover:bg-accent',
                    category === entry.id && 'bg-accent'
                  )}
                  onClick={() => {
                    setCategory(entry.id);
                    gridRef.current?.scrollTo({ top: 0 });
                  }}
                >
                  {entry.icon}
                </button>
              ))}
            </div>
          ) : null}

          <div ref={gridRef} className="min-w-0 flex-1 overflow-y-auto p-2">
            {isSearching ? (
              <>
                <p className="mb-2 px-1 text-xs font-medium text-muted-foreground">
                  {t('modulePages.selfroles.emojiSearchResults')}
                </p>
                {filteredServer.length === 0 && searchResults.length === 0 ? (
                  <div className="px-1 py-8 text-center text-sm text-muted-foreground">
                    {error ? (
                      <button type="button" className="underline" onClick={() => reload()}>
                        {t('common.saveError')}
                      </button>
                    ) : (
                      t('common.noResults')
                    )}
                  </div>
                ) : (
                  <div className="grid grid-cols-9 gap-0.5">
                    {filteredServer.map((emoji) => {
                      const encoded = formatCustomEmoji(emoji);
                      return (
                        <button
                          key={emoji.id}
                          type="button"
                          title={`:${emoji.name}:`}
                          className={cn(
                            'flex size-8 items-center justify-center rounded-md hover:bg-accent',
                            value === encoded && 'bg-accent'
                          )}
                          onMouseEnter={() =>
                            setHoverLabel({ native: encoded, name: `:${emoji.name}:` })
                          }
                          onClick={() => selectServerEmoji(emoji)}
                        >
                          {/* eslint-disable-next-line @next/next/no-img-element -- Discord CDN emoji preview */}
                          <img src={emoji.url} alt={emoji.name} className="size-5 object-contain" />
                        </button>
                      );
                    })}
                    {searchResults.map(({ emoji, native }) => (
                      <button
                        key={emoji.id}
                        type="button"
                        title={`:${emoji.id}:`}
                        className={cn(
                          'flex size-8 items-center justify-center rounded-md text-lg leading-none hover:bg-accent',
                          value === native && 'bg-accent'
                        )}
                        onMouseEnter={() =>
                          setHoverLabel({ native, name: `:${emoji.id}:` })
                        }
                        onClick={() => selectNative(native, `:${emoji.id}:`)}
                      >
                        {native}
                      </button>
                    ))}
                  </div>
                )}
              </>
            ) : category === 'server' ? (
              <>
                <p className="mb-2 px-1 text-xs font-medium text-muted-foreground">
                  {t('modulePages.selfroles.emojiServer')}
                </p>
                {error ? (
                  <div className="px-1 py-8 text-center text-sm text-muted-foreground">
                    <button type="button" className="underline" onClick={() => reload()}>
                      {t('common.saveError')}
                    </button>
                  </div>
                ) : filteredServer.length === 0 ? (
                  <div className="px-1 py-8 text-center text-sm text-muted-foreground">
                    {t('modulePages.selfroles.emojiServerEmpty')}
                  </div>
                ) : (
                  <div className="grid grid-cols-9 gap-0.5">
                    {filteredServer.map((emoji) => {
                      const encoded = formatCustomEmoji(emoji);
                      return (
                        <button
                          key={emoji.id}
                          type="button"
                          title={`:${emoji.name}:`}
                          className={cn(
                            'flex size-8 items-center justify-center rounded-md hover:bg-accent',
                            value === encoded && 'bg-accent'
                          )}
                          onMouseEnter={() =>
                            setHoverLabel({ native: encoded, name: `:${emoji.name}:` })
                          }
                          onClick={() => selectServerEmoji(emoji)}
                        >
                          {/* eslint-disable-next-line @next/next/no-img-element -- Discord CDN emoji preview */}
                          <img src={emoji.url} alt={emoji.name} className="size-5 object-contain" />
                        </button>
                      );
                    })}
                  </div>
                )}
              </>
            ) : activeUnicodeCategory ? (
              <>
                <p className="mb-2 px-1 text-xs font-medium text-muted-foreground">
                  {t(`modulePages.selfroles.emojiCategories.${activeUnicodeCategory.id}`)}
                </p>
                <div className="grid grid-cols-9 gap-0.5">
                  {activeUnicodeCategory.emojis.map((emoji) => {
                    const native = pickEmojiNative(emoji, skinTone);
                    return (
                      <button
                        key={emoji.id}
                        type="button"
                        title={`:${emoji.id}:`}
                        className={cn(
                          'flex size-8 items-center justify-center rounded-md text-lg leading-none hover:bg-accent',
                          value === native && 'bg-accent'
                        )}
                        onMouseEnter={() =>
                          setHoverLabel({ native, name: `:${emoji.id}:` })
                        }
                        onClick={() => selectNative(native, `:${emoji.id}:`)}
                      >
                        {native}
                      </button>
                    );
                  })}
                </div>
              </>
            ) : null}
          </div>
        </div>

        <div className="flex min-h-11 items-center gap-2 border-t border-border bg-muted/30 px-3 py-2">
          {footerPreview ? (
            <>
              <EmojiPreview value={footerPreview.native} className="size-6 text-xl" />
              <span className="truncate text-xs text-muted-foreground">{footerPreview.name}</span>
            </>
          ) : (
            <span className="text-xs text-muted-foreground">
              {t('modulePages.selfroles.emojiHoverHint')}
            </span>
          )}
        </div>
      </PopoverContent>
    </Popover>
  );
}
