/**
 * SetupBanner.test.tsx — regression tests for the gap-6 ['me'] freshness fix
 * (Phase 12 Plan 07 Task 2) plus the success-only dismissal contract.
 *
 * gap 6 mechanism (ii) — ['me'] staleness, NOT a backend linking gap:
 *   The first-login claim in upsertUser (apps/api/src/auth/user.ts) preserves the
 *   same users.id, so the wizard-stored CalDAV credential stays linked and the DB
 *   correctly reports needsProviderSetup=false after the operator authenticates.
 *   The bug was purely client-cache: a ['me'] entry populated BEFORE the claim
 *   (e.g. a pre-auth visit) served a stale needsProviderSetup=true for up to the
 *   5-minute staleTime, so the "Set up your calendar" banner kept showing.
 *   Fix: the banner's ['me'] query uses staleTime 0, so a stale cache entry is
 *   refetched on mount and the banner reflects the just-claimed credential.
 *
 * Tests cover:
 *   - needsProviderSetup=false → banner absent
 *   - needsProviderSetup=true  → banner present (no dismiss/X button)
 *   - staleTime 0: a pre-seeded stale ['me'] (needsProviderSetup=true) is refetched
 *     on mount; once fetchMe resolves needsProviderSetup=false, the banner hides
 *     (mirrors the post-wizard claim where the DB now reports false)
 */

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

// CredentialSheet is rendered (closed) by SetupBanner — mock it to avoid the
// real sheet's DOM/focus-trap complexity. We're testing banner visibility only.
vi.mock('./CredentialSheet.js', () => ({
  CredentialSheet: () => null,
}));

vi.mock('../api/client.js', () => ({
  fetchMe: vi.fn(),
}));

import { fetchMe } from '../api/client.js';
import type { Mock } from 'vitest';
import { SetupBanner } from './SetupBanner.js';

const mockFetchMe = fetchMe as Mock;

function makeQueryClient() {
  return new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  });
}

function renderBanner(queryClient: QueryClient) {
  return render(
    <QueryClientProvider client={queryClient}>
      <SetupBanner />
    </QueryClientProvider>,
  );
}

function meUser(needsProviderSetup: boolean) {
  return {
    user: {
      id: 1,
      displayName: 'Test User',
      color: '#4a90d9',
      isAdmin: false,
      needsProviderSetup,
    },
  };
}

describe('SetupBanner', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('does not render when needsProviderSetup is false', async () => {
    mockFetchMe.mockResolvedValue(meUser(false));

    const queryClient = makeQueryClient();
    renderBanner(queryClient);

    // Give the query a tick to resolve, then confirm the banner stays absent
    await new Promise((r) => setTimeout(r, 50));
    expect(screen.queryByText('Set up your calendar')).toBeNull();
  });

  it('renders the banner (no dismiss button) when needsProviderSetup is true', async () => {
    mockFetchMe.mockResolvedValue(meUser(true));

    const queryClient = makeQueryClient();
    renderBanner(queryClient);

    await waitFor(() => {
      expect(screen.getByText('Set up your calendar')).toBeInTheDocument();
    });

    // The ONLY action is "Set up now" — there is NO dismiss/X button (T-05-24).
    expect(screen.getByRole('button', { name: 'Set up now' })).toBeInTheDocument();
    expect(screen.queryByRole('button', { name: /dismiss/i })).toBeNull();
    expect(screen.queryByRole('button', { name: /close/i })).toBeNull();
  });

  // gap 6 regression: a stale ['me'] cache entry (needsProviderSetup=true, as it
  // would be from a pre-claim visit) must be refetched on mount because the query
  // uses staleTime 0. After the fresh fetch resolves needsProviderSetup=false (the
  // post-wizard-claim DB truth), the banner must hide.
  it('refetches a stale ["me"] on mount (staleTime 0) and hides the banner once needsProviderSetup is false', async () => {
    const queryClient = makeQueryClient();

    // Seed a STALE cache entry as if populated before the wizard claim.
    queryClient.setQueryData(['me'], meUser(true));

    // The server now reports the claimed credential → needsProviderSetup=false.
    mockFetchMe.mockResolvedValue(meUser(false));

    renderBanner(queryClient);

    // staleTime 0 → the seeded entry is stale → refetch fires on mount.
    await waitFor(() => {
      expect(mockFetchMe).toHaveBeenCalled();
    });

    // After the refetch resolves false, the banner must disappear.
    await waitFor(() => {
      expect(screen.queryByText('Set up your calendar')).toBeNull();
    });
  });
});
