/**
 * F5FE-005: Reports/Preview — copyShareLink must use hardened copyToClipboard.
 */
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { toast } from 'sonner';
import { describe, it, expect, vi, beforeEach } from 'vitest';

const { mockCopyToClipboard } = vi.hoisted(() => ({ mockCopyToClipboard: vi.fn() }));
vi.mock('@/lib/clipboard', () => ({ copyToClipboard: mockCopyToClipboard }));

vi.mock('sonner', () => ({
  toast: { success: vi.fn(), error: vi.fn() },
}));

const mockUsePolling = vi.fn();

vi.mock('@inertiajs/react', async () => {
  const actual = await vi.importActual('@inertiajs/react');
  return {
    ...actual,
    Head: ({ title }: { title: string }) => <title>{title}</title>,
    Link: ({ children, href }: { children: React.ReactNode; href: string }) => (
      <a href={href}>{children}</a>
    ),
    router: { visit: vi.fn(), post: vi.fn(), delete: vi.fn() },
    useForm: () => ({
      data: { expires_at: '', password: '' },
      setData: vi.fn(),
      post: vi.fn(),
      delete: vi.fn(),
      processing: false,
      reset: vi.fn(),
    }),
    usePage: vi.fn(() => ({
      props: {
        app_url: 'https://app.test',
        polling_interval_ms: 7500,
      },
    })),
  };
});

vi.mock('@/Layouts/DashboardLayout', () => ({
  default: ({ children }: { children: React.ReactNode }) => (
    <div data-testid="dashboard-layout">{children}</div>
  ),
}));
vi.mock('@/Components/Reports/ReportPreview', () => ({
  default: () => <div data-testid="report-preview" />,
}));
vi.mock('@/hooks/usePolling', () => ({
  usePolling: (...args: unknown[]) => mockUsePolling(...args),
}));
vi.mock('@/lib/format', () => ({
  formatNumber: (n: number) => String(n),
  formatDate: (d: string) => d,
}));

vi.stubGlobal('route', vi.fn((name: string, params: unknown) =>
  `/mock/${name}/${Array.isArray(params) ? params.join('/') : params ?? ''}`,
));

import Preview from './Preview';

const mockSite = { id: '1', name: 'Test Site', domain: 'test.com' };
const completedReport = {
  id: 42,
  name: 'Test Report',
  sections: ['traffic'],
  filters: {},
  status: 'completed',
  generated_at: '2026-04-10T10:00:00Z',
  created_at: '2026-04-10T09:00:00Z',
  has_pdf: false,
  data: null,
};
// REP-001/REP-002: share_url is the server-built URL. Legacy `token` field is gone.
const activeLink = {
  id: 1,
  share_url: 'https://app.test/shared-reports/share_tok_abc',
  expires_at: null,
  has_password: false,
  view_count: 3,
  last_viewed_at: null,
  is_expired: false,
};

describe('Reports/Preview — share link copy (F5FE-005)', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockCopyToClipboard.mockResolvedValue(true);
  });

  it('calls copyToClipboard with the full share URL when Copy Link is clicked', async () => {
    const user = userEvent.setup();
    render(
      <Preview site={mockSite} report={completedReport} sharedLinks={[activeLink]} />,
    );

    await user.click(screen.getByRole('button', { name: /copy link/i }));

    expect(mockCopyToClipboard).toHaveBeenCalledWith(
      expect.stringContaining('share_tok_abc'),
    );
  });

  it('fires toast.success when copyToClipboard returns true', async () => {
    mockCopyToClipboard.mockResolvedValue(true);
    const user = userEvent.setup();
    render(
      <Preview site={mockSite} report={completedReport} sharedLinks={[activeLink]} />,
    );

    await user.click(screen.getByRole('button', { name: /copy link/i }));
    await waitFor(() => expect(toast.success).toHaveBeenCalled());
    expect(toast.error).not.toHaveBeenCalled();
  });

  it('fires toast.error when copyToClipboard returns false (permission denied)', async () => {
    mockCopyToClipboard.mockResolvedValue(false);
    const user = userEvent.setup();
    render(
      <Preview site={mockSite} report={completedReport} sharedLinks={[activeLink]} />,
    );

    await user.click(screen.getByRole('button', { name: /copy link/i }));
    await waitFor(() => expect(toast.error).toHaveBeenCalled());
    expect(toast.success).not.toHaveBeenCalled();
  });

  it('does not throw when navigator.clipboard is undefined (insecure context)', async () => {
    mockCopyToClipboard.mockResolvedValue(false);
    const user = userEvent.setup();

    const originalClipboard = Object.getOwnPropertyDescriptor(navigator, 'clipboard');
    Object.defineProperty(navigator, 'clipboard', { value: undefined, configurable: true });

    render(
      <Preview site={mockSite} report={completedReport} sharedLinks={[activeLink]} />,
    );

    await expect(
      user.click(screen.getByRole('button', { name: /copy link/i })),
    ).resolves.not.toThrow();

    if (originalClipboard) {
      Object.defineProperty(navigator, 'clipboard', originalClipboard);
    }
  });
});
