import Document from '@tiptap/extension-document'
import Paragraph from '@tiptap/extension-paragraph'
import Text from '@tiptap/extension-text'
import { describe, expect, it } from 'vite-plus/test'

import { createNodeFromContent } from './createNodeFromContent.js'
import { getSchemaByResolvedExtensions } from './getSchemaByResolvedExtensions.js'

describe('createNodeFromContent', () => {
  it('creates a fragment from a schema and HTML content', () => {
    const content = '<p>Example Text</p>'

    const fragment = createNodeFromContent(
      content,
      getSchemaByResolvedExtensions([Document, Paragraph, Text]),
    )

    expect(fragment.toJSON()).toEqual([
      {
        type: 'paragraph',
        content: [
          {
            type: 'text',
            text: 'Example Text',
          },
        ],
      },
    ])
  })

  it('if `errorOnInvalidContent` is true, creates a fragment from a schema and HTML content', () => {
    const content = '<p>Example Text</p>'

    const fragment = createNodeFromContent(
      content,
      getSchemaByResolvedExtensions([Document, Paragraph, Text]),
      {
        errorOnInvalidContent: true,
      },
    )

    expect(fragment.toJSON()).toEqual([
      {
        type: 'paragraph',
        content: [
          {
            type: 'text',
            text: 'Example Text',
          },
        ],
      },
    ])
  })

  it('creates a fragment from a schema and JSON content', () => {
    const content = {
      type: 'paragraph',
      content: [
        {
          type: 'text',
          text: 'Example Text',
        },
      ],
    }

    const fragment = createNodeFromContent(
      content,
      getSchemaByResolvedExtensions([Document, Paragraph, Text]),
    )

    expect(fragment.toJSON()).toEqual({
      type: 'paragraph',
      content: [
        {
          type: 'text',
          text: 'Example Text',
        },
      ],
    })
  })

  it('if `errorOnInvalidContent` is true, creates a fragment from a schema and JSON content', () => {
    const content = {
      type: 'paragraph',
      content: [
        {
          type: 'text',
          text: 'Example Text',
        },
      ],
    }

    const fragment = createNodeFromContent(
      content,
      getSchemaByResolvedExtensions([Document, Paragraph, Text]),
      {
        errorOnInvalidContent: true,
      },
    )

    expect(fragment.toJSON()).toEqual({
      type: 'paragraph',
      content: [
        {
          type: 'text',
          text: 'Example Text',
        },
      ],
    })
  })

  it('creates a fragment from a schema and JSON array of content', () => {
    const content = [
      {
        type: 'paragraph',
        content: [
          {
            type: 'text',
            text: 'Example Text',
          },
        ],
      },
      {
        type: 'paragraph',
        content: [
          {
            type: 'text',
            text: 'More Text',
          },
        ],
      },
    ]

    const fragment = createNodeFromContent(
      content,
      getSchemaByResolvedExtensions([Document, Paragraph, Text]),
    )

    expect(fragment.toJSON()).toEqual([
      {
        type: 'paragraph',
        content: [
          {
            type: 'text',
            text: 'Example Text',
          },
        ],
      },
      {
        type: 'paragraph',
        content: [
          {
            type: 'text',
            text: 'More Text',
          },
        ],
      },
    ])
  })

  it('if `errorOnInvalidContent` is true, creates a fragment from a schema and JSON array of content', () => {
    const content = [
      {
        type: 'paragraph',
        content: [
          {
            type: 'text',
            text: 'Example Text',
          },
        ],
      },
      {
        type: 'paragraph',
        content: [
          {
            type: 'text',
            text: 'More Text',
          },
        ],
      },
    ]

    const fragment = createNodeFromContent(
      content,
      getSchemaByResolvedExtensions([Document, Paragraph, Text]),
      {
        errorOnInvalidContent: true,
      },
    )

    expect(fragment.toJSON()).toEqual([
      {
        type: 'paragraph',
        content: [
          {
            type: 'text',
            text: 'Example Text',
          },
        ],
      },
      {
        type: 'paragraph',
        content: [
          {
            type: 'text',
            text: 'More Text',
          },
        ],
      },
    ])
  })

  it('returns empty content when a schema does not have matching node types for JSON content', () => {
    const content = {
      type: 'non-existing-node-type',
      content: [
        {
          type: 'text',
          text: 'Example Text',
        },
      ],
    }

    const fragment = createNodeFromContent(
      content,
      getSchemaByResolvedExtensions([Document, Paragraph, Text]),
    )

    expect(fragment.toJSON()).toEqual(null)
  })

  it('returns empty content when a schema does not have matching node types for HTML content', () => {
    const content = '<non-existing-node-type>Example Text</non-existing-node-type>'

    const fragment = createNodeFromContent(
      content,
      getSchemaByResolvedExtensions([Document, Paragraph, Text]),
    )

    expect(fragment.toJSON()).toEqual([{ type: 'text', text: 'Example Text' }])
  })

  it('if `errorOnInvalidContent` is true, will throw an error when a schema does not have matching node types for HTML content', () => {
    const content = '<non-existing-node-type>Example Text</non-existing-node-type>'

    expect(() => {
      createNodeFromContent(content, getSchemaByResolvedExtensions([Document, Paragraph, Text]), {
        errorOnInvalidContent: true,
      })
    }).toThrow('[tiptap error]: Invalid HTML content')
  })

  it('if `errorOnInvalidContent` is true, will throw an error when a schema does not have matching node types for JSON content', () => {
    const content = {
      type: 'non-existing-node-type',
      content: [
        {
          type: 'text',
          text: 'Example Text',
        },
      ],
    }

    expect(() => {
      createNodeFromContent(content, getSchemaByResolvedExtensions([Document, Paragraph, Text]), {
        errorOnInvalidContent: true,
      })
    }).toThrow('[tiptap error]: Invalid JSON content')
  })

  it('if `errorOnInvalidContent` is true, will throw an error when a schema does not have matching mark types for JSON content', () => {
    const content = {
      type: 'paragraph',
      content: [
        {
          type: 'text',
          text: 'Example Text',
          marks: [
            {
              type: 'non-existing-mark-type',
            },
          ],
        },
      ],
    }

    expect(() => {
      createNodeFromContent(content, getSchemaByResolvedExtensions([Document, Paragraph, Text]), {
        errorOnInvalidContent: true,
      })
    }).toThrow('[tiptap error]: Invalid JSON content')
  })

  it('if `errorOnInvalidContent` is true, will throw an error, when the JSON content does not follow the nesting rules of the schema', () => {
    const content = {
      type: 'paragraph',
      content: [
        {
          type: 'paragraph',
          content: [
            {
              type: 'text',
              text: 'Example Text',
            },
          ],
        },
      ],
    }

    expect(() => {
      createNodeFromContent(content, getSchemaByResolvedExtensions([Document, Paragraph, Text]), {
        errorOnInvalidContent: true,
      })
    }).toThrow('[tiptap error]: Invalid JSON content')
  })

  // These look like a node or a fragment at a glance, but are still invalid JSON.
  it.each([
    ['an object type', { type: {} }],
    ['a null type', { type: null }],
    ['an array type', { type: [] }],
    ['a numeric size', { size: 5 }],
    ['no keys at all', {}],
  ])('returns empty content for JSON content with %s', (_label, content) => {
    const fragment = createNodeFromContent(
      content,
      getSchemaByResolvedExtensions([Document, Paragraph, Text]),
    )

    expect(fragment.toJSON()).toBeNull()
  })
})
