// @design-system: domain/FavoriteButton

'use client';

import { useCallback, type MouseEvent } from 'react';
import { cn } from '@/lib/cn';
import { Icon } from '@/components/ui/icons/Icon/index.js';
import { useT } from '@/lib/i18n/react';
import { useWishlistContext } from '@/features/wishlist/WishlistContext.js';
import { createOptimisticMutation } from '@/lib/query/optimistic.js';
import { qk } from '@/lib/query/keys.js';
import { getCsrfToken } from '@/lib/csrf';

export interface FavoriteButtonProps {
  vendorId: string;
  mirror?: boolean;
  /** Show visible text label alongside the icon. */
  showLabel?: boolean;
  className?: string;
  onAuthRequired?: () => void;
}

async function toggleFavoriteApi(vendorId: string) {
  const res = await fetch('/api/favorites/toggle', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', 'x-csrf-token': getCsrfToken() },
    body: JSON.stringify({ vendorId, notify: false }),
  });
  if (res.status === 401)
    throw Object.assign(new Error('AUTH_REQUIRED'), { code: 'AUTH_REQUIRED' });
  if (!res.ok) throw new Error('Toggle failed');
  return res.json() as Promise<{ ok: true; saved: boolean; notify: boolean }>;
}

type FavoritesSnapshot = Set<string>;

const useToggleFavorite = createOptimisticMutation<
  { ok: true; saved: boolean; notify: boolean },
  string,
  FavoritesSnapshot
>({
  mutationFn: toggleFavoriteApi,
  queryKey: qk.favorites(),
  optimisticUpdate: (prev, vendorId): FavoritesSnapshot => {
    const next = new Set(prev ?? new Set<string>());
    if (next.has(vendorId)) {
      next.delete(vendorId);
    } else {
      next.add(vendorId);
    }
    return next;
  },
  errorToast: () => 'Action failed — reverted',
});

export function FavoriteButton({
  vendorId,
  mirror = false,
  showLabel,
  className,
  onAuthRequired,
}: FavoriteButtonProps) {
  const t = useT('favorites');
  const { favoritedVendorIds, toggleVendor, isAuthenticated } = useWishlistContext();
  const isSaved = favoritedVendorIds.has(vendorId);
  const { mutate, isPending } = useToggleFavorite();

  const handleClick = useCallback(
    (e: MouseEvent) => {
      e.preventDefault();
      e.stopPropagation();
      if (!isAuthenticated) {
        onAuthRequired?.();
        return;
      }
      const nextSaved = !isSaved;
      toggleVendor(vendorId, nextSaved);
      mutate(vendorId, {
        onSuccess: (result) => {
          toggleVendor(vendorId, result.saved);
        },
        onError: (err: Error & { code?: string }) => {
          toggleVendor(vendorId, isSaved);
          if (err.code === 'AUTH_REQUIRED') onAuthRequired?.();
        },
      });
    },
    [vendorId, isSaved, onAuthRequired, mutate, toggleVendor, isAuthenticated],
  );

  return (
    <button
      type="button"
      aria-pressed={isSaved}
      aria-label={isSaved ? t('remove_from_favorites') : t('add_to_favorites')}
      disabled={isPending}
      onClick={handleClick}
      className={cn(
        'inline-flex items-center justify-center gap-1.5 rounded-full px-2 py-2 transition-colors',
        'focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--color-focus-ring)]',
        isSaved
          ? 'text-[var(--color-warning)] hover:text-[var(--color-warning-hover)]'
          : 'text-[var(--color-muted)] hover:text-[var(--color-foreground)]',
        isPending && 'cursor-wait opacity-50',
        className,
      )}
    >
      <Icon
        name="Star"
        size="sm"
        mirror={mirror}
        className={isSaved ? 'fill-[var(--color-warning)]' : ''}
      />
      <span className={showLabel ? 'text-sm' : 'sr-only'}>{t('button_label')}</span>
    </button>
  );
}
