import React, { createElement } from 'react'
import { render } from '@testing-library/react'
import RichText from '.'
import { ContentTree } from '@financial-times/content-tree'
import { Body } from '@financial-times/cp-content-pipeline-schema/src/resolvers/content-tree/Workarounds'

describe('<RichText />', () => {
  it('renders a content-tree using built-in components', () => {
    const tree: ContentTree.Body = {
      type: 'body',
      version: 1,
      children: [
        {
          type: 'paragraph',
          children: [{ type: 'text', value: 'This is some text.' }],
        } as ContentTree.Paragraph,
      ],
    }
    const structuredContent = { tree }

    const { asFragment } = render(
      <RichText structuredContent={structuredContent} />
    )
    expect(asFragment()).toMatchInlineSnapshot(`
      <DocumentFragment>
        <p>
          This is some text.
        </p>
      </DocumentFragment>
    `)
  })

  it('adds a key property to all child components', () => {
    jest.spyOn(React, 'createElement')
    const tree: Body = {
      type: 'body',
      version: 1,
      children: [
        {
          type: 'paragraph',
          children: [{ type: 'text', value: 'first paragraph' }],
        },
        {
          type: 'paragraph',
          children: [{ type: 'text', value: 'second paragraph' }],
        },
      ],
    }

    const structuredContent = {
      tree,
    }

    render(<RichText structuredContent={structuredContent} />)
    expect(createElement).toHaveBeenCalledWith(
      expect.any(Function),
      expect.objectContaining({ key: 0 })
    )
    expect(createElement).toHaveBeenCalledWith(
      expect.any(Function),
      expect.objectContaining({ key: 1 })
    )
  })

  it("ignores components that it doesn't have a mapping for", () => {
    jest.spyOn(console, 'warn').mockImplementation(() => {})

    const tree: Body = {
      type: 'body',
      version: 1,
      children: [
        {
          type: 'paragraph',
          children: [{ type: 'text', value: 'i should render' }],
        },
        {
          type: 'unknown',
          children: [
            { type: 'text', value: 'i should not render' } as ContentTree.Text,
          ],
        } as unknown as ContentTree.Paragraph,
      ],
    }

    const structuredContent = {
      tree,
    }

    const { getByText } = render(
      <RichText structuredContent={structuredContent} />
    )

    expect(getByText('i should render').tagName).toEqual('P')
    //wrapping component with react-testing-library is a div
    expect(getByText('i should not render').tagName).toEqual('DIV')
    expect(console.warn).toHaveBeenCalledWith(
      "couldn't find component for Content Tree node unknown, using fallback component instead (by default will render the node's children)"
    )
  })

  it('handles nested nodes with null children defensively', () => {
    const tree: Body = {
      type: 'body',
      version: 1,
      children: [
        {
          type: 'paragraph',
          children: null,
        } as unknown as ContentTree.Paragraph,
      ],
    }

    const structuredContent = {
      tree,
    }

    expect(() =>
      render(<RichText structuredContent={structuredContent} />)
    ).not.toThrow()
  })

  it('adds references as props', () => {
    const tree: Body = {
      type: 'body',
      version: 1,
      children: [
        {
          id: 'with-reference',
          type: 'tweet',
          data: {
            referenceIndex: 0,
          },
        } as ContentTree.Tweet,
        {
          id: 'without-reference',
          type: 'tweet',
        } as ContentTree.Tweet,
      ],
    }

    const structuredContent = {
      tree,
      references: [
        {
          type: 'tweet',
          id: 'with-reference',
          html: '<blockquote>tweet content</blockquote>',
        },
      ],
    }

    const { queryByText } = render(
      <RichText structuredContent={structuredContent} />
    )
    expect(queryByText('tweet content')).toBeTruthy()
    expect(queryByText('with-reference')).toBeFalsy()
    expect(queryByText('without-reference')).toBeTruthy()
  })
})
