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

const fetchGuestAiStylesMock = vi.fn();
const createGuestAiEditMock = vi.fn();
const fetchGuestAiEditStatusMock = vi.fn();

const translate = (key: string, options?: unknown, fallback?: string) => {
  if (typeof fallback === 'string') {
    return fallback;
  }

  if (typeof options === 'string') {
    return options;
  }

  return key;
};

vi.mock('../services/aiEditsApi', () => ({
  fetchGuestAiStyles: (...args: unknown[]) => fetchGuestAiStylesMock(...args),
  createGuestAiEdit: (...args: unknown[]) => createGuestAiEditMock(...args),
  fetchGuestAiEditStatus: (...args: unknown[]) => fetchGuestAiEditStatusMock(...args),
}));

vi.mock('@/shared/guest/i18n/useTranslation', () => ({
  useTranslation: () => ({
    t: translate,
  }),
}));

vi.mock('../lib/toast', () => ({
  pushGuestToast: vi.fn(),
}));

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

vi.mock('lucide-react', () => ({
  Copy: () => <span>copy</span>,
  Download: () => <span>download</span>,
  Loader2: () => <span>loader</span>,
  MessageSquare: () => <span>message</span>,
  RefreshCcw: () => <span>refresh</span>,
  Share2: () => <span>share</span>,
  Sparkles: () => <span>sparkles</span>,
  Wand2: () => <span>wand</span>,
  X: () => <span>x</span>,
}));

vi.mock('@tamagui/sheet', () => {
  const Sheet = ({ open, children }: { open?: boolean; children: React.ReactNode }) => (open ? <div>{children}</div> : null);

  Sheet.Overlay = ({ children }: { children?: React.ReactNode }) => <div>{children}</div>;
  Sheet.Frame = ({ children }: { children?: React.ReactNode }) => <div>{children}</div>;
  Sheet.Handle = ({ children }: { children?: React.ReactNode }) => <div>{children}</div>;

  return { Sheet };
});

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>
  ),
}));

import AiMagicEditSheet from '../components/AiMagicEditSheet';

describe('AiMagicEditSheet', () => {
  const originalOnLine = navigator.onLine;

  beforeEach(() => {
    fetchGuestAiStylesMock.mockReset();
    createGuestAiEditMock.mockReset();
    fetchGuestAiEditStatusMock.mockReset();
    Object.defineProperty(window.navigator, 'onLine', {
      configurable: true,
      value: true,
    });
  });

  afterEach(() => {
    vi.useRealTimers();
    Object.defineProperty(window.navigator, 'onLine', {
      configurable: true,
      value: originalOnLine,
    });
  });

  it('loads styles and creates an ai edit request', async () => {
    fetchGuestAiStylesMock.mockResolvedValue({
      data: [
        {
          id: 1,
          key: 'ghibli-soft',
          name: 'Ghibli Soft',
          description: 'Soft shading style',
        },
      ],
      meta: {},
    });

    createGuestAiEditMock.mockResolvedValue({
      duplicate: false,
      data: {
        id: 15,
        event_id: 2,
        photo_id: 7,
        status: 'succeeded',
        style: { id: 1, key: 'ghibli-soft', name: 'Ghibli Soft' },
        outputs: [{ id: 99, provider_url: 'https://example.com/ai.jpg', is_primary: true }],
      },
    });

    render(
      <AiMagicEditSheet
        open
        onOpenChange={vi.fn()}
        eventToken="event-token"
        photoId={7}
        originalImageUrl="/storage/original.jpg"
      />
    );

    expect(await screen.findByText('Ghibli Soft')).toBeInTheDocument();

    fireEvent.click(screen.getByText('Generate AI edit'));

    await waitFor(() => expect(createGuestAiEditMock).toHaveBeenCalledTimes(1));
    await waitFor(() => expect(screen.getByText('AI result')).toBeInTheDocument());
    expect(screen.getByText('Copy link')).toBeInTheDocument();
  });

  it('shows an error when style loading fails', async () => {
    fetchGuestAiStylesMock.mockRejectedValue(new Error('Styles not reachable'));

    render(
      <AiMagicEditSheet
        open
        onOpenChange={vi.fn()}
        eventToken="event-token"
        photoId={7}
        originalImageUrl="/storage/original.jpg"
      />
    );

    expect(await screen.findByText('Styles not reachable')).toBeInTheDocument();
  });

  it('pauses polling while offline and resumes after reconnect', async () => {
    Object.defineProperty(window.navigator, 'onLine', {
      configurable: true,
      value: false,
    });

    fetchGuestAiStylesMock.mockResolvedValue({
      data: [{ id: 1, key: 'ghibli-soft', name: 'Ghibli Soft' }],
      meta: {},
    });
    createGuestAiEditMock.mockResolvedValue({
      duplicate: false,
      data: {
        id: 22,
        event_id: 2,
        photo_id: 7,
        status: 'processing',
        style: { id: 1, key: 'ghibli-soft', name: 'Ghibli Soft' },
        outputs: [],
      },
    });
    fetchGuestAiEditStatusMock.mockResolvedValue({
      data: {
        id: 22,
        event_id: 2,
        photo_id: 7,
        status: 'succeeded',
        style: { id: 1, key: 'ghibli-soft', name: 'Ghibli Soft' },
        outputs: [{ id: 7, provider_url: 'https://example.com/generated.jpg', is_primary: true }],
      },
    });

    render(
      <AiMagicEditSheet
        open
        onOpenChange={vi.fn()}
        eventToken="event-token"
        photoId={7}
        originalImageUrl="/storage/original.jpg"
      />
    );

    expect(await screen.findByText('Ghibli Soft')).toBeInTheDocument();
    fireEvent.click(screen.getByText('Generate AI edit'));

    await waitFor(() => expect(createGuestAiEditMock).toHaveBeenCalledTimes(1));
    expect(await screen.findByText('You are offline. Status updates resume automatically when connection is back.')).toBeInTheDocument();

    vi.useFakeTimers();
    await vi.advanceTimersByTimeAsync(7000);
    expect(fetchGuestAiEditStatusMock).toHaveBeenCalledTimes(0);

    Object.defineProperty(window.navigator, 'onLine', {
      configurable: true,
      value: true,
    });
    await act(async () => {
      window.dispatchEvent(new Event('online'));
    });

    await act(async () => {
      await vi.advanceTimersByTimeAsync(3000);
    });
    await Promise.resolve();
    expect(fetchGuestAiEditStatusMock).toHaveBeenCalledTimes(1);
  });
});
