import { describe, expect, it, vi } from 'vitest'
import {
  MERMAID_BLOCK_TYPE,
  injectMermaidInBlocks,
  preProcessMermaidMarkdown,
} from './mermaidMarkdown'
import { serializeDurableEditorBlocks } from './editorDurableMarkdown'
import { TLDRAW_BLOCK_TYPE } from './tldrawMarkdown'

describe('mermaid markdown round-trip', () => {
  it('injects fenced Mermaid source into dedicated diagram blocks', () => {
    const markdown = [
      '```mermaid',
      'flowchart LR',
      '  A --> B',
      '```',
    ].join('\n')
    const preprocessed = preProcessMermaidMarkdown({ markdown })
    const blocks = [{
      type: 'paragraph',
      content: [{ type: 'text', text: preprocessed, styles: {} }],
      children: [],
    }]

    const [block] = injectMermaidInBlocks(blocks) as Array<{
      type: string
      props: { source: string; diagram: string }
    }>

    expect(block.type).toBe(MERMAID_BLOCK_TYPE)
    expect(block.props.source).toBe(markdown)
    expect(block.props.diagram).toBe('flowchart LR\n  A --> B\n')
  })

  it('restores token paragraphs split across inline text spans', () => {
    const markdown = [
      '```mermaid',
      'flowchart TD',
      '  A["alpha_beta"] --> B["ok"]',
      '```',
    ].join('\n')
    const token = preProcessMermaidMarkdown({ markdown })
    const splitPoint = Math.floor(token.length / 2)
    const [block] = injectMermaidInBlocks([{
      type: 'paragraph',
      content: [
        { type: 'text', text: token.slice(0, splitPoint), styles: {} },
        { type: 'text', text: token.slice(splitPoint), styles: { italic: true } },
      ],
      children: [],
    }]) as Array<{
      type: string
      props?: { source: string; diagram: string }
      content?: Array<{ text?: string }>
    }>

    expect(block.type).toBe(MERMAID_BLOCK_TYPE)
    expect(block.props).toMatchObject({
      source: markdown,
      diagram: 'flowchart TD\n  A["alpha_beta"] --> B["ok"]\n',
    })
  })

  it('does not erase non-text inline content while reading durable tokens', () => {
    const markdown = [
      '```mermaid',
      'flowchart TD',
      '  A --> B',
      '```',
    ].join('\n')
    const token = preProcessMermaidMarkdown({ markdown })
    const splitPoint = Math.floor(token.length / 2)
    const [block] = injectMermaidInBlocks([{
      type: 'paragraph',
      content: [
        { type: 'text', text: token.slice(0, splitPoint), styles: {} },
        { type: 'link', content: [{ type: 'text', text: 'external', styles: {} }] },
        { type: 'text', text: token.slice(splitPoint), styles: {} },
      ],
      children: [],
    }]) as Array<{
      type: string
      content?: Array<{ type?: string; text?: string }>
    }>

    expect(block.type).toBe('paragraph')
    expect(block.content).toHaveLength(3)
  })

  it('preserves multiple Mermaid blocks independently when serializing', () => {
    const editor = {
      blocksToMarkdownLossy: vi.fn((blocks: unknown[]) => {
        return (blocks as Array<{ content?: Array<{ text?: string }> }>)
          .map((block) => block.content?.map((item) => item.text ?? '').join('') ?? '')
          .join('\n\n')
      }),
    }
    const firstSource = '```mermaid\nflowchart TD\nA --> B\n```'
    const secondSource = '~~~mermaid\nsequenceDiagram\nAlice->>Bob: Hi\n~~~'
    const blocks = [
      { type: 'paragraph', content: [{ type: 'text', text: 'Intro' }], children: [] },
      { type: MERMAID_BLOCK_TYPE, props: { source: firstSource, diagram: 'flowchart TD\nA --> B\n' }, children: [] },
      { type: 'paragraph', content: [{ type: 'text', text: 'Between' }], children: [] },
      { type: MERMAID_BLOCK_TYPE, props: { source: secondSource, diagram: 'sequenceDiagram\nAlice->>Bob: Hi\n' }, children: [] },
    ]

    expect(serializeDurableEditorBlocks(editor, blocks)).toBe([
      'Intro',
      firstSource,
      'Between',
      secondSource,
    ].join('\n\n'))
  })

  it('injects parsed Mermaid code blocks into dedicated diagram blocks', () => {
    const [block] = injectMermaidInBlocks([{
      type: 'codeBlock',
      props: { language: 'mermaid' },
      content: [{ type: 'text', text: 'flowchart LR\n  A --> B', styles: {} }],
      children: [],
    }]) as Array<{
      type: string
      props: { source: string; diagram: string }
    }>

    expect(block.type).toBe(MERMAID_BLOCK_TYPE)
    expect(block.props.source).toBe('```mermaid\nflowchart LR\n  A --> B\n```')
    expect(block.props.diagram).toBe('flowchart LR\n  A --> B\n')
  })

  it('injects Mermaid-looking text code blocks when the parser drops the language', () => {
    const [block] = injectMermaidInBlocks([{
      type: 'codeBlock',
      props: { language: 'text' },
      content: [{
        type: 'text',
        text: [
          "%%{init: {'theme':'base'}}%%",
          'flowchart TD',
          '  A --> B',
        ].join('\n'),
        styles: {},
      }],
      children: [],
    }]) as Array<{
      type: string
      props: { source: string; diagram: string }
    }>

    expect(block.type).toBe(MERMAID_BLOCK_TYPE)
    expect(block.props.source).toContain('```mermaid\n')
    expect(block.props.diagram).toContain('flowchart TD\n  A --> B\n')
  })

  it('keeps ordinary text code blocks unchanged', () => {
    const [block] = injectMermaidInBlocks([{
      type: 'codeBlock',
      props: { language: 'text' },
      content: [{ type: 'text', text: 'const chart = "flowchart TD"', styles: {} }],
      children: [],
    }]) as Array<{ type: string }>

    expect(block.type).toBe('codeBlock')
  })

  it('leaves non-Mermaid and unclosed fences as normal Markdown', () => {
    const markdown = [
      '```ts',
      'const graph = "mermaid"',
      '```',
      '',
      '```mermaid',
      'flowchart LR',
    ].join('\n')

    expect(preProcessMermaidMarkdown({ markdown })).toBe(markdown)
  })

  it('serializes fallback source for Mermaid blocks created without original fence text', () => {
    const editor = { blocksToMarkdownLossy: vi.fn(() => '') }
    const blocks = [{
      type: MERMAID_BLOCK_TYPE,
      props: { source: '', diagram: 'flowchart LR\nA --> B' },
      children: [],
    }]

    expect(serializeDurableEditorBlocks(editor, blocks)).toBe(
      '```mermaid\nflowchart LR\nA --> B\n```',
    )
  })

  it('serializes tldraw blocks beside Mermaid and ordinary Markdown', () => {
    const editor = {
      blocksToMarkdownLossy: vi.fn((blocks: unknown[]) => {
        return (blocks as Array<{ content?: Array<{ text?: string }> }>)
          .map((block) => block.content?.map((item) => item.text ?? '').join('') ?? '')
          .join('\n\n')
      }),
    }
    const blocks = [
      { type: 'paragraph', content: [{ type: 'text', text: 'Intro' }], children: [] },
      { type: TLDRAW_BLOCK_TYPE, props: { boardId: 'map', height: '640', snapshot: '{ "store": {} }', width: '900' }, children: [] },
      { type: MERMAID_BLOCK_TYPE, props: { source: '', diagram: 'flowchart LR\nA --> B' }, children: [] },
    ]

    expect(serializeDurableEditorBlocks(editor, blocks)).toBe([
      'Intro',
      '```tldraw id="map" height="640" width="900"\n{ "store": {} }\n```',
      '```mermaid\nflowchart LR\nA --> B\n```',
    ].join('\n\n'))
  })
})
