import { describe, it, expect, beforeEach, afterEach } from 'vitest'
import { render, screen, fireEvent, waitFor } from '@testing-library/react'
import { WysiwygBuilderPage } from '@/components/wysiwyg/WysiwygBuilderPage'

const createObjectURL = global.URL.createObjectURL
const revokeObjectURL = global.URL.revokeObjectURL

describe('WYSIWYG Builder acceptance', () => {
  beforeEach(() => {
    // @ts-expect-error - test shim
    global.URL.createObjectURL = () => 'blob:mock'
    // @ts-expect-error - test shim
    global.URL.revokeObjectURL = () => {}
  })
  afterEach(() => {
    global.URL.createObjectURL = createObjectURL
    global.URL.revokeObjectURL = revokeObjectURL
  })

  it('Add → Edit → Export smoke', async () => {
    render(<WysiwygBuilderPage />)

    // Add Text
    fireEvent.click(screen.getByRole('button', { name: /Add Text/i }))
    const obj = await screen.findByTestId('obj-text-1')

    // Select and edit via Properties panel
    fireEvent.mouseDown(obj)
    const inputX = await screen.findByLabelText('X')
    fireEvent.change(inputX, { target: { value: '80' } })

    await waitFor(() => {
      // style left updated on canvas object (snap may apply, accept near 80)
      const left = parseInt((obj as HTMLElement).style.left || '0', 10)
      expect(Math.abs(left - 80)).toBeLessThanOrEqual(8)
    })

    // Export exists and triggers download
    const exportBtn = screen.getByRole('button', { name: /Export/i })
    fireEvent.click(exportBtn)
    // No throw means success; optionally wait a tick
    await new Promise((r) => setTimeout(r, 0))
  })

  it('uploads a local image and renders it via AssetManager path', async () => {
    render(<WysiwygBuilderPage />)

    const input = screen.getByTestId('wysiwyg-image-input') as HTMLInputElement
    const file = new File([new Uint8Array([1, 2, 3])], 'hero.png', { type: 'image/png' })

    fireEvent.change(input, { target: { files: [file] } })

    const img = await screen.findByRole('img')
    const src = (img as HTMLImageElement).src
    expect(src).toContain('/assets/')
  })

  it('syncs width/height/rotate from Properties panel to Canvas for a text object', async () => {
    render(<WysiwygBuilderPage />)

    // Add Text and select
    const addTextBtn = screen.getByRole('button', { name: /Add Text/i })
    fireEvent.click(addTextBtn)
    const [obj] = await screen.findAllByTestId(/obj-text-/)
    fireEvent.mouseDown(obj)

    // Edit width/height/rotate via Properties
    const inputW = await screen.findByLabelText('Width')
    const inputH = await screen.findByLabelText('Height')
    const inputR = await screen.findByLabelText('Rotate')

    fireEvent.change(inputW, { target: { value: '200' } })
    fireEvent.change(inputH, { target: { value: '60' } })
    fireEvent.change(inputR, { target: { value: '30' } })

    await waitFor(() => {
      const style = (obj as HTMLElement).style
      expect(style.width).toBe('200px')
      expect(style.height).toBe('60px')
      expect(style.transform).toContain('30deg')
    })
  })

  it('syncs text/button specific props from Properties to Canvas content', async () => {
    render(<WysiwygBuilderPage />)

    // Text
    fireEvent.click(screen.getByRole('button', { name: /Add Text/i }))
    const [textObj] = await screen.findAllByTestId(/obj-text-/)
    fireEvent.mouseDown(textObj)
    const textInput = await screen.findByLabelText('Text')
    fireEvent.change(textInput, { target: { value: 'Hello World' } })

    await waitFor(() => {
      expect(textObj).toHaveTextContent('Hello World')
    })

    // Button
    fireEvent.click(screen.getByRole('button', { name: /Add Button/i }))
    const [buttonObj] = await screen.findAllByTestId(/obj-button-/)
    fireEvent.mouseDown(buttonObj)
    const labelInput = await screen.findByLabelText('Label')
    fireEvent.change(labelInput, { target: { value: 'CTA' } })

    await waitFor(() => {
      expect(buttonObj).toHaveTextContent('CTA')
    })
  })
  
  it('supports Shift+click multiselect and group drag on the builder canvas', async () => {
    render(<WysiwygBuilderPage />)

    // 두 텍스트 추가
    const addTextBtn = screen.getByRole('button', { name: /Add Text/i })
    fireEvent.click(addTextBtn)
    fireEvent.click(addTextBtn)

    const [obj1, obj2] = await screen.findAllByTestId(/obj-text-/)

    // 첫 번째 선택
    fireEvent.mouseDown(obj1, { clientX: 70, clientY: 70 })
    // Shift+클릭으로 두 번째 추가 선택
    fireEvent.mouseDown(obj2, { clientX: 80, clientY: 120, shiftKey: true })

    // 멀티선택 상태에서 두 객체 모두 outline 스타일을 갖는지만 확인
    expect((obj1 as HTMLElement).style.outline).toContain('2px solid')
    expect((obj2 as HTMLElement).style.outline).toContain('2px solid')
  })

  it('supports marquee selection on the builder canvas', async () => {
    render(<WysiwygBuilderPage />)

    // 텍스트 두 개 배치
    const addTextBtn = screen.getByRole('button', { name: /Add Text/i })
    fireEvent.click(addTextBtn)
    fireEvent.click(addTextBtn)
    const [obj1, obj2] = await screen.findAllByTestId(/obj-text-/)

    // 마퀴 드래그 박스로 두 객체 모두 포함되도록 드래그
    const canvas = screen.getByTestId('wysiwyg-canvas')
    fireEvent.mouseDown(canvas, { clientX: 40, clientY: 40 })
    fireEvent.mouseMove(canvas, { clientX: 300, clientY: 300 })
    fireEvent.mouseUp(canvas)

    // 선택된 두 객체가 모두 outline 스타일을 갖는지 확인
    expect((obj1 as HTMLElement).style.outline).toContain('2px solid')
    expect((obj2 as HTMLElement).style.outline).toContain('2px solid')
  })
})
