import { describe, it, expect, beforeEach, vi } from 'vitest'
import { render, screen, waitFor } from '@testing-library/react'
import { MemoryRouter } from 'react-router-dom'
import { HelmetProvider } from 'react-helmet-async'

import { AcceptInvitePage } from '../AcceptInvitePage'
import { inviteApi } from '@/api/invite'
import {
  __resetAppConfigCache,
  __setAppConfigCache,
} from '@/hooks/useAppConfig'

vi.mock('@/api/invite', () => ({
  inviteApi: {
    lookupAccountInvite: vi.fn(),
    validateCode: vi.fn(),
  },
}))

vi.mock('@/store/authStore', () => ({
  useAuthStore: () => ({
    register: vi.fn().mockResolvedValue(undefined),
    isLoading: false,
    error: null,
    clearError: vi.fn(),
  }),
}))

function renderPage(initialPath: string) {
  return render(
    <HelmetProvider>
      <MemoryRouter initialEntries={[initialPath]}>
        <AcceptInvitePage />
      </MemoryRouter>
    </HelmetProvider>,
  )
}

describe('AcceptInvitePage', () => {
  beforeEach(() => {
    __resetAppConfigCache()
    __setAppConfigCache({
      self_serve_enabled: true,
      oauth_providers: ['google', 'microsoft'],
    })
    vi.clearAllMocks()
  })

  it('shows account name + locked email + accept buttons for a valid code', async () => {
    vi.mocked(inviteApi.lookupAccountInvite).mockResolvedValue({
      account_name: 'Acme MSP',
      inviter_name: 'Alice Owner',
      invited_email: 'bob@acme.example',
      role: 'engineer',
    })

    renderPage('/accept-invite?code=VALIDINVITECODE0011223344556677')

    // Inviter context (also confirms the lookup completed and rendered)
    await waitFor(() => {
      expect(
        screen.getByText(/Alice Owner invited you as engineer/),
      ).toBeInTheDocument()
    })
    // Account name surfaces in the heading line.
    expect(
      screen.getByText((_content, node) => {
        return (
          node?.tagName.toLowerCase() === 'span' &&
          /Acme MSP/.test(node.textContent || '')
        )
      }),
    ).toBeInTheDocument()

    // Locked email — not an editable input
    const emailDisplay = screen.getByTestId('invited-email')
    expect(emailDisplay.tagName.toLowerCase()).not.toBe('input')
    expect(emailDisplay).toHaveTextContent('bob@acme.example')
    expect(screen.queryByLabelText(/email address/i)).not.toBeInTheDocument()

    // OAuth buttons + password submit all rendered
    expect(screen.getByTestId('oauth-google')).toBeInTheDocument()
    expect(screen.getByTestId('oauth-microsoft')).toBeInTheDocument()
    expect(screen.getByTestId('accept-submit')).toBeInTheDocument()
    expect(screen.getByTestId('accept-submit')).toHaveTextContent(/Join Acme MSP/)

    expect(inviteApi.lookupAccountInvite).toHaveBeenCalledWith(
      'VALIDINVITECODE0011223344556677',
    )
  })

  it('shows resend message + mailto link for an invalid invite code', async () => {
    vi.mocked(inviteApi.lookupAccountInvite).mockRejectedValue(
      Object.assign(new Error('not found'), {
        response: {
          status: 404,
          data: { detail: { error: 'invite_invalid_or_expired_or_revoked' } },
        },
      }),
    )

    renderPage('/accept-invite?code=BADCODE')

    await waitFor(() => {
      expect(
        screen.getByText(/This invite is no longer valid/i),
      ).toBeInTheDocument()
    })
    expect(
      screen.getByText(/Ask the person who invited you to resend it/i),
    ).toBeInTheDocument()

    const resendLink = screen.getByRole('link', { name: /Email your inviter/i })
    expect(resendLink).toHaveAttribute(
      'href',
      expect.stringMatching(/^mailto:/),
    )

    // No accept form rendered when invite is invalid.
    expect(screen.queryByTestId('accept-submit')).not.toBeInTheDocument()
    expect(screen.queryByTestId('oauth-google')).not.toBeInTheDocument()
  })
})
