// `<SettingRow>` render-shape tests. The primitive backs every
// settings-screen toggle / radio / value row across the app, so a
// regression here would visibly distort half a dozen screens at once.
//
// Pins:
//   1. Without `onPress`, the row renders as a static `Card` (no
//      `Pressable` wrapper). That's the "presentational only" case
//      — used by the disabled or read-only rows in the storage page.
//   2. With `onPress`, the row is wrapped in a `Pressable` and the
//      onPress fires when the wrapper is pressed.
//   3. The trailing slot is rendered verbatim — Switch / check icon /
//      value+chevron all flow through unchanged.
//   4. When the row is pressable, accessibility props (role / state /
//      label / hint) reach the `Pressable`.

import React from 'react'
import { act, create, type ReactTestRenderer } from 'react-test-renderer'

jest.mock('tamagui', () => {
  const ReactM = require('react') as typeof import('react')
  const passthrough =
    (tag: string) =>
    ({ children, ...rest }: { children?: React.ReactNode; [k: string]: any }) =>
      ReactM.createElement(tag, rest, children)
  // Make the `styled(View, {...})` factory return a passthrough too — the
  // Card and FieldText primitives use it.
  const styled = (_base: any, _opts: any) => passthrough('Styled')
  return {
    View: passthrough('View'),
    YStack: passthrough('YStack'),
    XStack: passthrough('XStack'),
    Text: passthrough('Text'),
    Paragraph: passthrough('Paragraph'),
    styled,
    useTheme: () => ({}),
  }
})

type SettingRowModule = typeof import('../src/components/ui/SettingRow')
let SettingRow: SettingRowModule['SettingRow']

beforeAll(() => {
  SettingRow = (require('../src/components/ui/SettingRow') as SettingRowModule)
    .SettingRow
})

function renderTree(node: React.ReactElement): { root: ReactTestRenderer; tree: any } {
  let root: ReactTestRenderer = null as unknown as ReactTestRenderer
  act(() => {
    root = create(node)
  })
  return { root, tree: root.toJSON() }
}

function findByType(node: any, type: string): any[] {
  const out: any[] = []
  const walk = (n: any) => {
    if (!n) return
    if (Array.isArray(n)) {
      n.forEach(walk)
      return
    }
    if (typeof n !== 'object') return
    if (n.type === type) out.push(n)
    walk(n.children)
  }
  walk(node)
  return out
}

describe('<SettingRow>', () => {
  it('renders without a Pressable wrapper when onPress is omitted', () => {
    const { tree } = renderTree(
      <SettingRow
        title="Static row"
        description="Read-only"
        trailing={React.createElement('Sentinel-trailing', null, null)}
      />
    )
    // No interactive wrapper at the root — the Pressable is mounted only
    // when the caller wires onPress.
    expect(findByType(tree, 'RCTView').length).toBe(0)
    expect(findByType(tree, 'Sentinel-trailing').length).toBe(1)
  })

  it('wraps in a Pressable when onPress is provided and fires on press', () => {
    const onPress = jest.fn()
    const { root } = renderTree(
      <SettingRow
        title="Tap me"
        description="Toggles a thing"
        onPress={onPress}
        trailing={React.createElement('Sentinel-trailing', null, null)}
      />
    )
    // react-native's Pressable renders a host node — find it by the
    // accessibilityRole we attached at the SettingRow layer.
    const pressables = root.root.findAll(
      (n) => !!n.props && n.props.accessibilityLabel === 'Tap me'
    )
    expect(pressables.length).toBeGreaterThan(0)
    act(() => {
      ;(pressables[0].props as { onPress?: () => void }).onPress?.()
    })
    expect(onPress).toHaveBeenCalledTimes(1)
  })

  it('forwards accessibility role / state / hint to the Pressable wrapper', () => {
    const { root } = renderTree(
      <SettingRow
        title="Light"
        description="Daytime theme"
        onPress={() => undefined}
        accessibility={{
          role: 'radio',
          state: { selected: true },
          hint: 'Sets the app to light theme',
        }}
        trailing={null}
      />
    )
    // `react-native`'s `Pressable` re-emits the role on its host children too,
    // so role-only matching can over-match. Pin the outermost node by also
    // requiring `accessibilityState` (only the wrapper carries the full
    // a11y prop bag we pass in).
    const pressables = root.root.findAll(
      (n) =>
        !!n.props &&
        (n.props as any).accessibilityRole === 'radio' &&
        (n.props as any).accessibilityState !== undefined &&
        typeof (n.props as any).onPress === 'function'
    )
    expect(pressables.length).toBe(1)
    const props = pressables[0].props as any
    expect(props.accessibilityState).toEqual({ selected: true })
    expect(props.accessibilityHint).toBe('Sets the app to light theme')
    expect(props.accessibilityLabel).toBe('Light')
  })

  it('renders the trailing slot verbatim', () => {
    const { tree } = renderTree(
      <SettingRow
        title="With trailing"
        trailing={React.createElement('MarkerNode', { 'data-marker': 'yes' }, null)}
      />
    )
    const matches = findByType(tree, 'MarkerNode')
    expect(matches.length).toBe(1)
  })

  it('omits the description block when no description is provided', () => {
    const { tree } = renderTree(<SettingRow title="Title only" />)
    // The geometry is still valid — the row just collapses to a single
    // line. The render shouldn't crash and the title should still be in
    // the tree.
    const stringified = JSON.stringify(tree)
    expect(stringified).toContain('Title only')
  })
})
