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

vi.mock('@inertiajs/react', () => ({
  router: {
    post: vi.fn(),
  },
}));

vi.mock('@/hooks/useGscSyncNotification', () => ({
  useGscSyncNotification: vi.fn(() => ({
    requestPermissionWithFeedback: vi.fn(),
  })),
}));

vi.stubGlobal('route', vi.fn(() => '/mock-route'));

import { GscSyncStatusCompact } from './GscSyncStatusCompact';

// ─── COPY-001: sync wait copy ─────────────────────────────────────────────────

describe('GscSyncStatusCompact — COPY-001: sync wait copy', () => {
  it('does NOT show anxiety-amplifying "This can take a while…" copy', () => {
    render(
      <GscSyncStatusCompact
        syncProgressPct={20}
        syncStartedAt={new Date(Date.now() - 60_000).toISOString()}
      />,
    );
    expect(screen.queryByText(/this can take a while/i)).not.toBeInTheDocument();
  });

  it('shows a time-bounded expectation string for a fresh sync', () => {
    render(
      <GscSyncStatusCompact
        syncProgressPct={10}
        syncStartedAt={new Date(Date.now() - 30_000).toISOString()}
      />,
    );
    // Should contain a concrete time estimate
    expect(screen.getByText(/usually 2[–-]5 minutes/i)).toBeInTheDocument();
  });

  it('shows "taking a bit longer" message when elapsed > 3 minutes', () => {
    // elapsed = 4 minutes
    const fourMinutesAgo = new Date(Date.now() - 4 * 60 * 1000).toISOString();
    render(
      <GscSyncStatusCompact syncProgressPct={30} syncStartedAt={fourMinutesAgo} />,
    );
    expect(
      screen.getByText(/taking a bit longer than usual/i),
    ).toBeInTheDocument();
  });

  it('shows the baseline-building copy below stage description', () => {
    render(
      <GscSyncStatusCompact
        syncProgressPct={50}
        syncStartedAt={new Date(Date.now() - 90_000).toISOString()}
      />,
    );
    expect(screen.getByText(/building your traffic baseline/i)).toBeInTheDocument();
  });
});

// ─── A11Y-003: elapsed timer live region ─────────────────────────────────────

describe('GscSyncStatusCompact — A11Y-003: elapsed timer verbosity', () => {
  beforeEach(() => {
    vi.useFakeTimers();
  });
  afterEach(() => {
    vi.useRealTimers();
  });

  it('elapsed span does NOT have aria-live (screen reader verbosity)', () => {
    render(
      <GscSyncStatusCompact
        syncProgressPct={20}
        syncStartedAt={new Date(Date.now() - 60_000).toISOString()}
      />,
    );
    const elapsed = screen.queryByTestId('sync-elapsed');
    // If present, must not have aria-live
    if (elapsed) {
      expect(elapsed.getAttribute('aria-live')).toBeNull();
    }
  });

  it('elapsed counter updates at most once per 30 seconds (not every second)', () => {
    const startedAt = new Date(Date.now() - 60_000).toISOString();
    render(
      <GscSyncStatusCompact syncProgressPct={20} syncStartedAt={startedAt} />,
    );

    // Fast-forward 5 seconds — should NOT have triggered multiple updates
    // (with 60s interval, the initial render + 5s advance = same display)
    const initialElapsed = screen.queryByTestId('sync-elapsed')?.textContent;

    act(() => {
      vi.advanceTimersByTime(5_000);
    });

    const afterFiveSeconds = screen.queryByTestId('sync-elapsed')?.textContent;
    // Text should be the same (interval is 60s, not 1s)
    expect(afterFiveSeconds).toBe(initialElapsed);
  });
});

// ─── UX-218: minute-only elapsed display ─────────────────────────────────────

describe('GscSyncStatusCompact — UX-218: minute-only elapsed display', () => {
  beforeEach(() => {
    vi.useFakeTimers();
  });
  afterEach(() => {
    vi.useRealTimers();
  });

  it('shows "less than a minute" for a sync started < 60 s ago', () => {
    // 30 seconds ago — should show "less than a minute", never "30s"
    const startedAt = new Date(Date.now() - 30_000).toISOString();
    render(<GscSyncStatusCompact syncProgressPct={5} syncStartedAt={startedAt} />);

    const elapsed = screen.queryByTestId('sync-elapsed');
    if (elapsed) {
      expect(elapsed.textContent).toMatch(/less than a minute/i);
      // Must NOT show seconds precision
      expect(elapsed.textContent).not.toMatch(/\d+s/i);
    }
  });

  it('shows "about Nm" for a sync 3.5 minutes old', () => {
    const startedAt = new Date(Date.now() - 3.5 * 60_000).toISOString();
    render(<GscSyncStatusCompact syncProgressPct={40} syncStartedAt={startedAt} />);

    const elapsed = screen.getByTestId('sync-elapsed');
    expect(elapsed.textContent).toMatch(/about 3m/i);
  });

  it('advances from "about 3m" to "about 4m" after one 60s tick', () => {
    const startedAt = new Date(Date.now() - 3.5 * 60_000).toISOString();
    render(<GscSyncStatusCompact syncProgressPct={40} syncStartedAt={startedAt} />);

    expect(screen.getByTestId('sync-elapsed').textContent).toMatch(/about 3m/i);

    act(() => {
      vi.advanceTimersByTime(60_000);
    });

    expect(screen.getByTestId('sync-elapsed').textContent).toMatch(/about 4m/i);
  });

  it('never shows raw seconds in the elapsed label', () => {
    const startedAt = new Date(Date.now() - 2 * 60_000 - 45_000).toISOString();
    render(<GscSyncStatusCompact syncProgressPct={25} syncStartedAt={startedAt} />);

    const elapsed = screen.queryByTestId('sync-elapsed');
    if (elapsed) {
      // Seconds-precision format "Xm YYs" must not appear
      expect(elapsed.textContent).not.toMatch(/\d+m \d{2}s/i);
    }
  });
});
