import { render, screen, fireEvent } from '@testing-library/react'
import { GlobalStylesPanel } from '../GlobalStylesPanel'
import type { GlobalStyles } from '../../types'
import { DEFAULT_GLOBAL_STYLES } from '../../types'

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

const baseStyles: GlobalStyles = {
  backgroundColor: '#f3f4f6',
  contentWidth: 600,
  fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif',
  theme: 'clean',
}

function renderPanel(styles: GlobalStyles = baseStyles, onChange = jest.fn()) {
  return render(<GlobalStylesPanel globalStyles={styles} onChange={onChange} />)
}

// ---------------------------------------------------------------------------
// Renders style inputs
// ---------------------------------------------------------------------------

describe('GlobalStylesPanel — renders style inputs', () => {
  it('renders the Background Color label', () => {
    renderPanel()
    expect(screen.getByText('Background Color')).toBeInTheDocument()
  })

  it('renders the background color text input with current value', () => {
    renderPanel()
    const inputs = screen.getAllByDisplayValue('#f3f4f6')
    // At least one text input shows the background color value
    expect(inputs.length).toBeGreaterThan(0)
  })

  it('renders the Content Width input', () => {
    renderPanel()
    expect(screen.getByText('Content Width (px)')).toBeInTheDocument()
  })

  it('renders content width number input with current value', () => {
    renderPanel()
    expect(screen.getByDisplayValue('600')).toBeInTheDocument()
  })

  it('renders the Font Family label', () => {
    renderPanel()
    expect(screen.getByText('Font Family')).toBeInTheDocument()
  })

  it('renders the Themes section heading', () => {
    renderPanel()
    expect(screen.getByText('Themes')).toBeInTheDocument()
  })

  it('renders Clean theme preset button', () => {
    renderPanel()
    expect(screen.getByText('Clean')).toBeInTheDocument()
  })

  it('renders Minimal theme preset button', () => {
    renderPanel()
    expect(screen.getByText('Minimal')).toBeInTheDocument()
  })

  it('renders Bold theme preset button', () => {
    renderPanel()
    expect(screen.getByText('Bold')).toBeInTheDocument()
  })

  it('renders theme description text for each preset', () => {
    renderPanel()
    expect(screen.getByText('White background with subtle gray accents')).toBeInTheDocument()
    expect(screen.getByText('Pure white with thin borders')).toBeInTheDocument()
    expect(screen.getByText('Dark background with vivid accents')).toBeInTheDocument()
  })

  it('renders color picker input for background color', () => {
    const { container } = renderPanel()
    const colorInput = container.querySelector('input[type="color"]') as HTMLInputElement
    expect(colorInput).toBeInTheDocument()
    expect(colorInput.value).toBe('#f3f4f6')
  })

  it('renders Email Design section heading', () => {
    renderPanel()
    expect(screen.getByText('Email Design')).toBeInTheDocument()
  })
})

// ---------------------------------------------------------------------------
// onChange fires with updated styles
// ---------------------------------------------------------------------------

describe('GlobalStylesPanel — onChange fires with updated styles', () => {
  it('calls onChange when background color text input changes', () => {
    const onChange = jest.fn()
    renderPanel(baseStyles, onChange)
    const inputs = screen.getAllByDisplayValue('#f3f4f6')
    // Find the text input (not color picker) — the Input component has type="text" by default
    const textInput = inputs.find(
      (el) => (el as HTMLInputElement).type !== 'color'
    ) as HTMLInputElement
    fireEvent.change(textInput, { target: { value: '#ffffff' } })
    expect(onChange).toHaveBeenCalledTimes(1)
    expect(onChange).toHaveBeenCalledWith(
      expect.objectContaining({ backgroundColor: '#ffffff' })
    )
  })

  it('calls onChange when color picker changes', () => {
    const onChange = jest.fn()
    const { container } = renderPanel(baseStyles, onChange)
    const colorInput = container.querySelector('input[type="color"]') as HTMLInputElement
    fireEvent.change(colorInput, { target: { value: '#123456' } })
    expect(onChange).toHaveBeenCalledTimes(1)
    expect(onChange).toHaveBeenCalledWith(
      expect.objectContaining({ backgroundColor: '#123456' })
    )
  })

  it('calls onChange with parsed integer when content width changes', () => {
    const onChange = jest.fn()
    renderPanel(baseStyles, onChange)
    const widthInput = screen.getByDisplayValue('600')
    fireEvent.change(widthInput, { target: { value: '700' } })
    expect(onChange).toHaveBeenCalledTimes(1)
    expect(onChange).toHaveBeenCalledWith(
      expect.objectContaining({ contentWidth: 700 })
    )
  })

  it('defaults contentWidth to 600 when invalid value entered', () => {
    const onChange = jest.fn()
    renderPanel(baseStyles, onChange)
    const widthInput = screen.getByDisplayValue('600')
    fireEvent.change(widthInput, { target: { value: 'abc' } })
    expect(onChange).toHaveBeenCalledWith(
      expect.objectContaining({ contentWidth: 600 })
    )
  })

  it('calls onChange with Clean theme preset globalStyles on Clean button click', () => {
    const onChange = jest.fn()
    renderPanel(baseStyles, onChange)
    fireEvent.click(screen.getByText('Clean'))
    expect(onChange).toHaveBeenCalledTimes(1)
    const called = onChange.mock.calls[0][0] as GlobalStyles
    expect(called.theme).toBe('clean')
  })

  it('calls onChange with Minimal theme preset globalStyles on Minimal button click', () => {
    const onChange = jest.fn()
    renderPanel(baseStyles, onChange)
    fireEvent.click(screen.getByText('Minimal'))
    expect(onChange).toHaveBeenCalledTimes(1)
    const called = onChange.mock.calls[0][0] as GlobalStyles
    expect(called.theme).toBe('minimal')
    expect(called.backgroundColor).toBe('#ffffff')
  })

  it('calls onChange with Bold theme preset globalStyles on Bold button click', () => {
    const onChange = jest.fn()
    renderPanel(baseStyles, onChange)
    fireEvent.click(screen.getByText('Bold'))
    expect(onChange).toHaveBeenCalledTimes(1)
    const called = onChange.mock.calls[0][0] as GlobalStyles
    expect(called.theme).toBe('bold')
    expect(called.backgroundColor).toBe('#1f2937')
  })

  it('preserves all other globalStyles fields when only backgroundColor changes', () => {
    const onChange = jest.fn()
    renderPanel(baseStyles, onChange)
    const inputs = screen.getAllByDisplayValue('#f3f4f6')
    const textInput = inputs.find(
      (el) => (el as HTMLInputElement).type !== 'color'
    ) as HTMLInputElement
    fireEvent.change(textInput, { target: { value: '#aaaaaa' } })
    const result = onChange.mock.calls[0][0] as GlobalStyles
    expect(result.contentWidth).toBe(baseStyles.contentWidth)
    expect(result.fontFamily).toBe(baseStyles.fontFamily)
    expect(result.theme).toBe(baseStyles.theme)
  })
})

// ---------------------------------------------------------------------------
// Active theme highlighting
// ---------------------------------------------------------------------------

describe('GlobalStylesPanel — active theme highlight', () => {
  it('applies active border class to the currently selected theme button', () => {
    const { container } = renderPanel({ ...baseStyles, theme: 'clean' })
    const cleanButton = screen.getByText('Clean').closest('button') as HTMLElement
    expect(cleanButton.className).toContain('border-primary')
  })

  it('does not apply active border class to non-selected theme buttons', () => {
    const { container } = renderPanel({ ...baseStyles, theme: 'clean' })
    const minimalButton = screen.getByText('Minimal').closest('button') as HTMLElement
    expect(minimalButton.className).not.toContain('border-primary')
  })

  it('applies active class to Minimal when minimal theme is selected', () => {
    renderPanel({ ...baseStyles, theme: 'minimal' })
    const minimalButton = screen.getByText('Minimal').closest('button') as HTMLElement
    expect(minimalButton.className).toContain('border-primary')
  })
})

// ---------------------------------------------------------------------------
// DEFAULT_GLOBAL_STYLES as initial values
// ---------------------------------------------------------------------------

describe('GlobalStylesPanel — DEFAULT_GLOBAL_STYLES compatibility', () => {
  it('renders correctly with DEFAULT_GLOBAL_STYLES', () => {
    expect(() => renderPanel(DEFAULT_GLOBAL_STYLES)).not.toThrow()
  })

  it('shows default content width 600', () => {
    renderPanel(DEFAULT_GLOBAL_STYLES)
    expect(screen.getByDisplayValue('600')).toBeInTheDocument()
  })
})
