import React from 'react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';

const setSearchParamsMock = vi.fn();
const pushGuestToastMock = vi.fn();
const mockEventData = {
  token: 'demo',
  event: { name: 'Demo Event', capabilities: { ai_styling: false } },
};

vi.mock('react-router-dom', () => ({
  useNavigate: () => vi.fn(),
  useSearchParams: () => [new URLSearchParams('photo=123'), setSearchParamsMock],
}));

vi.mock('../context/EventDataContext', () => ({
  useEventData: () => mockEventData,
}));

vi.mock('../hooks/usePollGalleryDelta', () => ({
  usePollGalleryDelta: () => ({ data: { photos: [] } }),
}));

vi.mock('../hooks/usePollStats', () => ({
  usePollStats: () => ({ stats: { onlineGuests: 0, guestCount: 0, likesCount: 0 } }),
}));

vi.mock('@/shared/guest/i18n/useTranslation', () => ({
  useTranslation: () => ({
    t: (key: string, options?: unknown, fallback?: string) => {
      if (typeof fallback === 'string') return fallback;
      if (typeof options === 'string') return options;
      return key;
    },
  }),
}));

vi.mock('@/shared/guest/i18n/LocaleContext', () => ({
  useLocale: () => ({ locale: 'de' }),
}));

vi.mock('../lib/guestTheme', () => ({
  useGuestThemeVariant: () => ({ isDark: false }),
}));

vi.mock('../lib/bento', () => ({
  getBentoSurfaceTokens: () => ({
    backgroundColor: '#fff',
    borderColor: '#eee',
    borderBottomColor: '#ddd',
    shadow: 'none',
  }),
}));

const fetchGalleryMock = vi.fn().mockResolvedValue({ data: [] });
const fetchPhotoMock = vi.fn().mockRejectedValue(Object.assign(new Error('not found'), { status: 404 }));

vi.mock('../services/photosApi', () => ({
  fetchGallery: (...args: unknown[]) => fetchGalleryMock(...args),
  fetchPhoto: (...args: unknown[]) => fetchPhotoMock(...args),
  likePhoto: vi.fn(),
  unlikePhoto: vi.fn(),
  createPhotoShareLink: vi.fn(),
  deletePhoto: vi.fn(),
}));

vi.mock('../components/AppShell', () => ({
  default: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}));

vi.mock('../components/PhotoFrameTile', () => ({
  default: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}));

vi.mock('../components/ShareSheet', () => ({
  default: () => null,
}));

vi.mock('../components/AiMagicEditSheet', () => ({
  default: () => null,
}));

vi.mock('../lib/toast', () => ({
  pushGuestToast: (...args: unknown[]) => pushGuestToastMock(...args),
}));

vi.mock('@tamagui/stacks', () => ({
  YStack: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
  XStack: ({ children }: { children: React.ReactNode }) => <div>{children}</div>,
}));

vi.mock('@tamagui/text', () => ({
  SizableText: ({ children }: { children: React.ReactNode }) => <span>{children}</span>,
}));

vi.mock('@tamagui/button', () => ({
  Button: ({ children, onPress, ...rest }: { children: React.ReactNode; onPress?: () => void }) => (
    <button type="button" onClick={onPress} {...rest}>
      {children}
    </button>
  ),
}));

vi.mock('lucide-react', () => ({
  Camera: () => <span>camera</span>,
  Sparkles: () => <span>sparkles</span>,
  Heart: () => <span>heart</span>,
  Share2: () => <span>share</span>,
  ChevronLeft: () => <span>left</span>,
  ChevronRight: () => <span>right</span>,
  Loader2: () => <span>loader</span>,
  Download: () => <span>download</span>,
  X: () => <span>x</span>,
  Trash2: () => <span>trash</span>,
}));

import GalleryScreen from '../screens/GalleryScreen';

describe('GalleryScreen', () => {
  beforeEach(() => {
    setSearchParamsMock.mockClear();
    pushGuestToastMock.mockClear();
    fetchGalleryMock.mockReset();
    fetchPhotoMock.mockReset();
    mockEventData.token = 'demo';
    mockEventData.event = { name: 'Demo Event', capabilities: { ai_styling: false } };
  });

  afterEach(() => {
    vi.useRealTimers();
  });

  it('clears the photo param and shows a warning when lightbox fails to load', async () => {
    vi.useFakeTimers();
    fetchGalleryMock.mockResolvedValue({ data: [] });
    fetchPhotoMock.mockRejectedValue(Object.assign(new Error('not found'), { status: 404 }));
    render(<GalleryScreen />);

    for (let i = 0; i < 7; i += 1) {
      await vi.advanceTimersByTimeAsync(1500);
      await Promise.resolve();
    }
    await vi.advanceTimersByTimeAsync(1000);
    await vi.runAllTimersAsync();
    await Promise.resolve();
    expect(pushGuestToastMock).toHaveBeenCalled();
    expect(setSearchParamsMock).toHaveBeenCalled();

    const [params] = setSearchParamsMock.mock.calls.at(-1) ?? [];
    const search = params instanceof URLSearchParams ? params : new URLSearchParams(params);
    expect(search.get('photo')).toBeNull();
  });

  it('keeps lightbox open when a seeded photo exists but fetch fails', async () => {
    vi.useFakeTimers();
    fetchGalleryMock.mockResolvedValue({
      data: [{ id: 123, thumbnail_url: '/storage/demo.jpg', likes_count: 2 }],
    });
    fetchPhotoMock.mockRejectedValue(Object.assign(new Error('not found'), { status: 404 }));

    render(<GalleryScreen />);

    await Promise.resolve();
    await vi.runAllTimersAsync();
    expect(fetchGalleryMock).toHaveBeenCalled();
    expect(fetchPhotoMock).toHaveBeenCalled();

    await vi.advanceTimersByTimeAsync(1000);
    expect(setSearchParamsMock).not.toHaveBeenCalled();
    expect(pushGuestToastMock).not.toHaveBeenCalled();
  });

  it('does not show ai magic edit action when ai styling is not entitled', async () => {
    fetchGalleryMock.mockResolvedValue({
      data: [{ id: 123, thumbnail_url: '/storage/demo.jpg', likes_count: 2 }],
    });
    fetchPhotoMock.mockRejectedValue(Object.assign(new Error('not found'), { status: 404 }));

    render(<GalleryScreen />);

    await waitFor(() => expect(fetchGalleryMock).toHaveBeenCalled());
    await waitFor(() =>
      expect(screen.queryByLabelText('AI Magic Edit')).not.toBeInTheDocument()
    );
  });

  it('keeps ai magic edit action hidden while rollout flag is disabled', async () => {
    mockEventData.event = { name: 'Demo Event', capabilities: { ai_styling: true } };
    fetchGalleryMock.mockResolvedValue({
      data: [{ id: 123, thumbnail_url: '/storage/demo.jpg', likes_count: 2 }],
    });
    fetchPhotoMock.mockRejectedValue(Object.assign(new Error('not found'), { status: 404 }));

    render(<GalleryScreen />);

    await waitFor(() => expect(fetchGalleryMock).toHaveBeenCalled());
    await waitFor(() =>
      expect(screen.queryByLabelText('AI Magic Edit')).not.toBeInTheDocument()
    );
  });
});
