import { render, screen, fireEvent } from '@testing-library/react'
import { BlockRenderer } from '../BlockRenderer'
import type {
  TextBlock,
  MetricsBlock,
  DividerBlock,
  CTABlock,
  ImageBlock,
  SpacerBlock,
  SocialBlock,
  HeaderBlock,
  FooterBlock,
  Block,
} from '../types'

// ---------------------------------------------------------------------------
// Fixtures
// ---------------------------------------------------------------------------

const textBlock: TextBlock = {
  id: 'text-1',
  type: 'text',
  content: '<p>Hello dispatch</p>',
}

const metricsBlock: MetricsBlock = {
  id: 'metrics-1',
  type: 'metrics',
  title: 'Dispatch Metrics',
  metrics: [
    { id: 'm1', label: 'ARR', value: '$5k', changeType: 'positive' },
  ],
  columns: 2,
}

const dividerBlock: DividerBlock = {
  id: 'divider-1',
  type: 'divider',
  dividerStyle: 'solid',
  color: '#d1d5db',
  thickness: 1,
  width: 100,
}

const ctaBlock: CTABlock = {
  id: 'cta-1',
  type: 'cta',
  text: 'Dispatch CTA',
  url: 'https://example.com',
  buttonColor: '#2563eb',
  textColor: '#ffffff',
  borderRadius: 6,
  paddingH: 24,
  paddingV: 12,
  alignment: 'center',
}

const imageBlock: ImageBlock = {
  id: 'image-1',
  type: 'image',
  url: 'https://example.com/img.png',
  alt: 'dispatch img',
  alignment: 'center',
  width: 100,
}

const spacerBlock: SpacerBlock = {
  id: 'spacer-1',
  type: 'spacer',
  height: 40,
}

const socialBlock: SocialBlock = {
  id: 'social-1',
  type: 'social',
  links: [{ id: 'sl1', platform: 'linkedin', url: 'https://linkedin.com/test' }],
  iconSize: 24,
  alignment: 'center',
}

const headerBlock: HeaderBlock = {
  id: 'header-1',
  type: 'header',
  companyName: 'DispatchCo',
  alignment: 'center',
}

const footerBlock: FooterBlock = {
  id: 'footer-1',
  type: 'footer',
  companyName: 'DispatchCo',
  showUnsubscribe: false,
  alignment: 'center',
}

// ---------------------------------------------------------------------------
// Helper
// ---------------------------------------------------------------------------

function renderBlock(block: Block, editing = false) {
  return render(
    <BlockRenderer
      block={block}
      editing={editing}
      onChange={jest.fn()}
      mergeFields={[]}
    />
  )
}

// ---------------------------------------------------------------------------
// Dispatches to correct editor
// ---------------------------------------------------------------------------

describe('BlockRenderer — dispatches to correct block editor', () => {
  it('renders TextBlockEditor for text block (view mode shows .prose)', () => {
    const { container } = renderBlock(textBlock, false)
    expect(container.querySelector('.prose')).toBeInTheDocument()
  })

  it('renders TextBlockEditor for text block (editing mode shows contentEditable)', () => {
    const { container } = renderBlock(textBlock, true)
    expect(container.querySelector('[contenteditable="true"]')).toBeInTheDocument()
  })

  it('renders MetricsBlockEditor for metrics block — shows title', () => {
    renderBlock(metricsBlock, false)
    expect(screen.getByText('Dispatch Metrics')).toBeInTheDocument()
  })

  it('renders MetricsBlockEditor for metrics block — shows metric value', () => {
    renderBlock(metricsBlock, false)
    expect(screen.getByText('$5k')).toBeInTheDocument()
  })

  it('renders DividerBlockEditor for divider block — shows hr element', () => {
    const { container } = renderBlock(dividerBlock, false)
    expect(container.querySelector('hr')).toBeInTheDocument()
  })

  it('renders ButtonBlockEditor for cta block — shows button text', () => {
    renderBlock(ctaBlock, false)
    expect(screen.getByText('Dispatch CTA')).toBeInTheDocument()
  })

  it('renders ImageBlockEditor for image block — shows img element', () => {
    const { container } = renderBlock(imageBlock, false)
    expect(container.querySelector('img')).toBeInTheDocument()
  })

  it('renders SpacerBlockEditor for spacer block — shows correct height in editing mode', () => {
    renderBlock(spacerBlock, true)
    expect(screen.getByText('40px')).toBeInTheDocument()
  })

  it('renders SocialBlockEditor for social block — shows social icon wrapper', () => {
    const { container } = renderBlock(socialBlock, false)
    expect(container.querySelector('div')).toBeInTheDocument()
    // LinkedIn link present since url is set
    expect(container.querySelector('a')).toBeInTheDocument()
  })

  it('renders HeaderBlockEditor for header block — shows company name', () => {
    renderBlock(headerBlock, false)
    expect(screen.getByText('DispatchCo')).toBeInTheDocument()
  })

  it('renders FooterBlockEditor for footer block — shows company name', () => {
    renderBlock(footerBlock, false)
    // Footer renders company name
    expect(screen.getByText('DispatchCo')).toBeInTheDocument()
  })
})

// ---------------------------------------------------------------------------
// editing prop is forwarded correctly
// ---------------------------------------------------------------------------

describe('BlockRenderer — editing prop forwarding', () => {
  it('text block: editing=false renders view mode (.prose)', () => {
    const { container } = renderBlock(textBlock, false)
    expect(container.querySelector('.prose')).toBeInTheDocument()
    expect(container.querySelector('[contenteditable="true"]')).not.toBeInTheDocument()
  })

  it('text block: editing=true renders edit mode (contentEditable)', () => {
    const { container } = renderBlock(textBlock, true)
    expect(container.querySelector('[contenteditable="true"]')).toBeInTheDocument()
  })

  it('spacer block: editing=false renders plain div without label text', () => {
    const { container } = renderBlock(spacerBlock, false)
    // view mode renders a plain height div, no "px" label
    expect(screen.queryByText('40px')).not.toBeInTheDocument()
    const spacer = container.querySelector('div') as HTMLElement
    expect(spacer.style.height).toBe('40px')
  })

  it('spacer block: editing=true shows height label', () => {
    renderBlock(spacerBlock, true)
    expect(screen.getByText('40px')).toBeInTheDocument()
  })

  it('image block: editing=true shows placeholder when url empty', () => {
    const emptyImage: ImageBlock = { ...imageBlock, url: '' }
    render(
      <BlockRenderer
        block={emptyImage}
        editing={true}
        onChange={jest.fn()}
        mergeFields={[]}
      />
    )
    expect(screen.getByText('Click to set image URL')).toBeInTheDocument()
  })
})

// ---------------------------------------------------------------------------
// onChange is forwarded through to child editors
// ---------------------------------------------------------------------------

describe('BlockRenderer — onChange forwarding', () => {
  it('text block: onChange is called when contentEditable blurs', () => {
    const onChange = jest.fn()
    const { container } = render(
      <BlockRenderer
        block={textBlock}
        editing={true}
        onChange={onChange}
        mergeFields={[]}
      />
    )
    const editable = container.querySelector('[contenteditable="true"]') as HTMLElement
    fireEvent.blur(editable)
    expect(onChange).toHaveBeenCalledTimes(1)
    expect(onChange).toHaveBeenCalledWith(
      expect.objectContaining({ type: 'text', id: 'text-1' })
    )
  })

  it('metrics block: onChange is called when title input changes', () => {
    const onChange = jest.fn()
    render(
      <BlockRenderer
        block={metricsBlock}
        editing={true}
        onChange={onChange}
        mergeFields={[]}
      />
    )
    const titleInput = screen.getByPlaceholderText('Key Metrics')
    fireEvent.change(titleInput, { target: { value: 'New Title' } })
    expect(onChange).toHaveBeenCalledTimes(1)
    expect(onChange).toHaveBeenCalledWith(
      expect.objectContaining({ type: 'metrics', title: 'New Title' })
    )
  })
})

// ---------------------------------------------------------------------------
// renderTextEditor prop is forwarded to TextBlockEditor
// ---------------------------------------------------------------------------

describe('BlockRenderer — renderTextEditor prop', () => {
  it('renders custom editor when renderTextEditor is provided for text block', () => {
    const renderTextEditor = jest.fn(() => <div data-testid="custom-rich-editor" />)
    render(
      <BlockRenderer
        block={textBlock}
        editing={true}
        onChange={jest.fn()}
        mergeFields={[]}
        renderTextEditor={renderTextEditor}
      />
    )
    expect(screen.getByTestId('custom-rich-editor')).toBeInTheDocument()
    expect(renderTextEditor).toHaveBeenCalledWith(
      expect.objectContaining({
        content: textBlock.content,
      })
    )
  })

  it('renderTextEditor is ignored for non-text block types', () => {
    const renderTextEditor = jest.fn(() => <div data-testid="custom-rich-editor" />)
    render(
      <BlockRenderer
        block={dividerBlock}
        editing={true}
        onChange={jest.fn()}
        mergeFields={[]}
        renderTextEditor={renderTextEditor}
      />
    )
    // DividerBlockEditor should render instead
    expect(screen.queryByTestId('custom-rich-editor')).not.toBeInTheDocument()
    const { container } = render(
      <BlockRenderer
        block={dividerBlock}
        editing={true}
        onChange={jest.fn()}
        mergeFields={[]}
        renderTextEditor={renderTextEditor}
      />
    )
    expect(container.querySelector('hr')).toBeInTheDocument()
    expect(renderTextEditor).not.toHaveBeenCalled()
  })
})

// ---------------------------------------------------------------------------
// mergeFields prop does not cause errors
// ---------------------------------------------------------------------------

describe('BlockRenderer — mergeFields prop', () => {
  it('renders without error when mergeFields is empty', () => {
    expect(() => renderBlock(textBlock, false)).not.toThrow()
  })

  it('renders without error when mergeFields has items', () => {
    expect(() =>
      render(
        <BlockRenderer
          block={textBlock}
          editing={false}
          onChange={jest.fn()}
          mergeFields={[{ key: '{{name}}', label: 'Name', example: 'John', category: 'contact' }]}
        />
      )
    ).not.toThrow()
  })
})
