'use client';

import type { DiscordChannelOption } from '@nexumi/shared';
import { Check, ChevronsUpDown, X } from 'lucide-react';
import { useMemo, useState } from 'react';
import { useTranslations } from '@/components/locale-provider';
import { Button } from '@/components/ui/button';
import {
  Command,
  CommandEmpty,
  CommandGroup,
  CommandInput,
  CommandItem,
  CommandList
} from '@/components/ui/command';
import { DiscordResourceMultiSelect } from '@/components/ui/discord-resource-multi-select';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { useGuildChannels } from '@/hooks/use-guild-channels';
import { CHANNEL_TYPES, channelPrefix, type ChannelKind } from '@/lib/discord-channel-types';
import { cn } from '@/lib/utils';

function filterChannels(
  channels: DiscordChannelOption[],
  kinds: ChannelKind[] | undefined
): DiscordChannelOption[] {
  if (!kinds || kinds.length === 0) {
    return channels;
  }
  const allowed = new Set<number>(kinds.flatMap((kind) => [...CHANNEL_TYPES[kind]]));
  return channels.filter((channel) => allowed.has(channel.type));
}

function toOptions(channels: DiscordChannelOption[]) {
  return channels.map((channel) => ({
    id: channel.id,
    name: channel.name,
    prefix: channelPrefix(channel.type)
  }));
}

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

interface ChannelSelectBaseProps {
  guildId: string;
  kinds?: ChannelKind[];
  disabled?: boolean;
  placeholder?: string;
}

interface DiscordChannelSelectProps extends ChannelSelectBaseProps {
  value: string;
  onChange: (next: string) => void;
  allowClear?: boolean;
}

/** Single channel picker (searchable). Empty string = cleared. */
export function DiscordChannelSelect({
  guildId,
  value,
  onChange,
  kinds = ['messageable'],
  disabled = false,
  placeholder,
  allowClear = true
}: DiscordChannelSelectProps) {
  const t = useTranslations();
  const { channels, loading, error, reload } = useGuildChannels(guildId);
  const [open, setOpen] = useState(false);
  const [query, setQuery] = useState('');

  const options = useMemo(() => toOptions(filterChannels(channels, kinds)), [channels, kinds]);
  const selected = options.find((option) => option.id === value);
  const filtered = useMemo(() => {
    const needle = normalizeSearch(query);
    if (!needle) {
      return options;
    }
    return options.filter((option) => {
      const haystack = normalizeSearch(`${option.prefix}${option.name} ${option.id}`);
      return haystack.includes(needle);
    });
  }, [options, query]);

  const triggerLabel = selected
    ? `${selected.prefix}${selected.name}`
    : value
      ? `#${value}`
      : (placeholder ?? t('modulePages.commands.channelSearchPlaceholder'));

  return (
    <Popover
      open={open}
      onOpenChange={(next) => {
        setOpen(next);
        if (!next) {
          setQuery('');
        }
      }}
    >
      <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('truncate', !selected && !value && 'text-muted-foreground')}>
            {triggerLabel}
          </span>
          <ChevronsUpDown className="ml-2 size-4 shrink-0 opacity-50" />
        </Button>
      </PopoverTrigger>
      <PopoverContent className="p-0" align="start">
        <Command shouldFilter={false}>
          <CommandInput
            value={query}
            onValueChange={setQuery}
            placeholder={placeholder ?? t('modulePages.commands.channelSearchPlaceholder')}
          />
          <CommandList>
            <CommandEmpty>
              {error ? (
                <button type="button" className="underline" onClick={() => reload()}>
                  {t('common.saveError')}
                </button>
              ) : (
                t('common.noResults')
              )}
            </CommandEmpty>
            <CommandGroup>
              {allowClear && value ? (
                <CommandItem
                  value="__clear__"
                  onSelect={() => {
                    onChange('');
                    setOpen(false);
                  }}
                >
                  <X className="size-4 opacity-60" />
                  <span className="text-muted-foreground">{t('common.clearSelection')}</span>
                </CommandItem>
              ) : null}
              {filtered.map((option) => (
                <CommandItem
                  key={option.id}
                  value={`${option.name} ${option.id}`}
                  onSelect={() => {
                    onChange(option.id);
                    setOpen(false);
                  }}
                >
                  <Check className={cn('size-4', value === option.id ? 'opacity-100' : 'opacity-0')} />
                  <span className="truncate">
                    {option.prefix}
                    {option.name}
                  </span>
                </CommandItem>
              ))}
            </CommandGroup>
          </CommandList>
        </Command>
      </PopoverContent>
    </Popover>
  );
}

interface DiscordChannelMultiSelectProps extends ChannelSelectBaseProps {
  value: string[];
  onChange: (next: string[]) => void;
}

/** Multi channel picker (searchable), same UX as Commands. */
export function DiscordChannelMultiSelect({
  guildId,
  value,
  onChange,
  kinds = ['messageable'],
  disabled = false,
  placeholder
}: DiscordChannelMultiSelectProps) {
  const t = useTranslations();
  const { channels, loading } = useGuildChannels(guildId);
  const options = useMemo(() => toOptions(filterChannels(channels, kinds)), [channels, kinds]);

  return (
    <DiscordResourceMultiSelect
      options={options}
      value={value}
      onChange={onChange}
      disabled={disabled || loading}
      placeholder={placeholder ?? t('modulePages.commands.channelSearchPlaceholder')}
    />
  );
}
