// @vitest-environment happy-dom
import '@testing-library/jest-dom/vitest'
import { render, screen, fireEvent, waitFor, act } from '@testing-library/react'
import { vi, describe, it, expect, beforeEach } from 'vitest'
import { UpdateCredentialsDialog } from './UpdateCredentialsDialog'

// Captured mutation options from the latest useMutation call
let capturedOnSuccess: (() => void) | undefined
let capturedOnError: ((err: { message: string }) => void) | undefined

const mockMutate = vi.fn()
const mockInvalidate = vi.fn()

vi.mock('@/lib/trpc', () => ({
  trpc: {
    updateAccountAuth: {
      useMutation: vi.fn((opts?: {
        onSuccess?: () => void
        onError?: (err: { message: string }) => void
      }) => {
        capturedOnSuccess = opts?.onSuccess
        capturedOnError = opts?.onError
        return { mutate: mockMutate, isPending: false }
      }),
    },
    useUtils: vi.fn(() => ({
      systemHealthCheck: { invalidate: mockInvalidate },
    })),
  },
}))

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

import { toast } from 'sonner'

const account = {
  id: 'acc-1',
  email: 'alice@example.com',
  provider: 'claude',
}

describe('UpdateCredentialsDialog', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    capturedOnSuccess = undefined
    capturedOnError = undefined
  })

  // Test 1: Renders with read-only identity
  it('renders account email and provider as non-editable', () => {
    render(<UpdateCredentialsDialog open={true} onOpenChange={vi.fn()} account={account} />)

    expect(screen.getByText('alice@example.com')).toBeInTheDocument()
    expect(screen.getByText('claude')).toBeInTheDocument()

    // They must not be editable inputs
    const emailEl = screen.getByText('alice@example.com')
    expect(emailEl.tagName).not.toBe('INPUT')
    const providerEl = screen.getByText('claude')
    expect(providerEl.tagName).not.toBe('INPUT')
  })

  // Test 2: Tab A submit — empty token — shows Required, no mutation
  it('shows Required error when token is empty and does not call mutation', async () => {
    render(<UpdateCredentialsDialog open={true} onOpenChange={vi.fn()} account={account} />)

    fireEvent.click(screen.getByRole('button', { name: /update credentials/i }))

    await waitFor(() => expect(screen.getByText('Required')).toBeInTheDocument())
    expect(mockMutate).not.toHaveBeenCalled()
  })

  // Test 3: Tab A submit — valid token — calls mutation with correct input
  it('calls mutation with correct token input', async () => {
    render(<UpdateCredentialsDialog open={true} onOpenChange={vi.fn()} account={account} />)

    fireEvent.change(screen.getByLabelText(/setup token/i), { target: { value: 'test-token' } })
    fireEvent.click(screen.getByRole('button', { name: /update credentials/i }))

    await waitFor(() => expect(mockMutate).toHaveBeenCalledWith({
      id: 'acc-1',
      configJson: '{"hasCompletedOnboarding":true}',
      credentials: '{"claudeAiOauth":{"accessToken":"test-token"}}',
    }))
  })

  // Test 4: Tab B submit — invalid config JSON — shows "Invalid JSON", no mutation
  it('shows Invalid JSON error for bad config JSON and does not call mutation', async () => {
    render(<UpdateCredentialsDialog open={true} onOpenChange={vi.fn()} account={account} />)

    fireEvent.click(screen.getByText('By credentials JSON'))
    fireEvent.change(screen.getByLabelText(/config json/i), { target: { value: 'bad json' } })
    fireEvent.click(screen.getByRole('button', { name: /update credentials/i }))

    await waitFor(() => expect(screen.getByText('Invalid JSON')).toBeInTheDocument())
    expect(mockMutate).not.toHaveBeenCalled()
  })

  // Test 5: Tab B submit — empty textareas — calls mutation with '{}' defaults
  it("calls mutation with '{}' defaults when credentials textareas are empty", async () => {
    render(<UpdateCredentialsDialog open={true} onOpenChange={vi.fn()} account={account} />)

    fireEvent.click(screen.getByText('By credentials JSON'))
    fireEvent.click(screen.getByRole('button', { name: /update credentials/i }))

    await waitFor(() => expect(mockMutate).toHaveBeenCalledWith({
      id: 'acc-1',
      configJson: '{}',
      credentials: '{}',
    }))
  })

  // Test 6: Server error — shows inline error, dialog stays open
  it('shows server error inline and does not close dialog on error', async () => {
    const onOpenChange = vi.fn()
    render(<UpdateCredentialsDialog open={true} onOpenChange={onOpenChange} account={account} />)

    fireEvent.change(screen.getByLabelText(/setup token/i), { target: { value: 'some-token' } })
    fireEvent.click(screen.getByRole('button', { name: /update credentials/i }))

    act(() => {
      capturedOnError?.({ message: 'Token expired' })
    })

    await waitFor(() => expect(screen.getByText('Token expired')).toBeInTheDocument())
    expect(onOpenChange).not.toHaveBeenCalled()
  })

  // Test 7: Success — toasts and closes dialog
  it('shows success toast and closes dialog on success', async () => {
    const onOpenChange = vi.fn()
    render(<UpdateCredentialsDialog open={true} onOpenChange={onOpenChange} account={account} />)

    fireEvent.change(screen.getByLabelText(/setup token/i), { target: { value: 'some-token' } })
    fireEvent.click(screen.getByRole('button', { name: /update credentials/i }))

    act(() => {
      capturedOnSuccess?.()
    })

    await waitFor(() => {
      expect(toast.success).toHaveBeenCalledWith('Credentials updated for alice@example.com.')
      expect(onOpenChange).toHaveBeenCalledWith(false)
    })
  })

  // Test 8: State reset on re-open
  it('resets state when dialog is re-opened', async () => {
    const { rerender } = render(
      <UpdateCredentialsDialog open={true} onOpenChange={vi.fn()} account={account} />
    )

    // Type a token and trigger server error
    fireEvent.change(screen.getByLabelText(/setup token/i), { target: { value: 'old-token' } })
    fireEvent.click(screen.getByRole('button', { name: /update credentials/i }))

    act(() => {
      capturedOnError?.({ message: 'Server error' })
    })
    await waitFor(() => expect(screen.getByText('Server error')).toBeInTheDocument())

    // Close dialog
    rerender(<UpdateCredentialsDialog open={false} onOpenChange={vi.fn()} account={account} />)
    // Re-open dialog
    rerender(<UpdateCredentialsDialog open={true} onOpenChange={vi.fn()} account={account} />)

    // Token should be cleared
    const tokenInput = screen.getByLabelText(/setup token/i) as HTMLInputElement
    expect(tokenInput.value).toBe('')
    // Server error should be gone
    expect(screen.queryByText('Server error')).not.toBeInTheDocument()
  })
})
