/**
 * Tests del overlay de automatización en BpmnCanvas.
 *
 * bpmn-js se mockea completamente. El elementRegistry.get() devuelve
 * elementos con width=100 para verificar el posicionamiento left=width-12=88.
 */

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { render, act } from '@testing-library/react'

// ─── Mock de bpmn-js ──────────────────────────────────────────────────────────

const mockOverlaysAdd = vi.fn()
const mockOverlaysRemove = vi.fn()
const mockCanvasZoom = vi.fn()
const mockRegForEach = vi.fn()
// elementRegistry.get() devuelve un elemento con width=100 (nodo estándar de bpmn-js)
const mockRegGet = vi.fn().mockReturnValue({ width: 100, height: 80 })

vi.mock('bpmn-js/lib/Viewer', () => ({
  default: class MockViewer {
    importXML = vi.fn().mockResolvedValue({ warnings: [] })

    get = vi.fn().mockImplementation((svc: string) => {
      if (svc === 'overlays') return { add: mockOverlaysAdd, remove: mockOverlaysRemove }
      if (svc === 'canvas')   return { zoom: mockCanvasZoom, addMarker: vi.fn(), removeMarker: vi.fn() }
      if (svc === 'elementRegistry') return { forEach: mockRegForEach, get: mockRegGet }
      if (svc === 'eventBus') return { on: vi.fn(), off: vi.fn() }
      return {}
    })

    destroy = vi.fn()
  },
}))

import { BpmnCanvas } from '@/features/workspace/BpmnCanvas'

// ─── Fixture ──────────────────────────────────────────────────────────────────

const SIMPLE_XML = `<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" targetNamespace="test">
  <process id="p"><startEvent id="s"/></process>
</definitions>`

// Con width=100 de mockRegGet, left = 100 - 12 = 88
const EXPECTED_LEFT = 88
const EXPECTED_TOP = -8

beforeEach(() => {
  vi.clearAllMocks()
  mockRegGet.mockReturnValue({ width: 100, height: 80 })
})

async function flush() {
  await act(async () => { await new Promise((r) => setTimeout(r, 0)) })
}

// ─── Tests ────────────────────────────────────────────────────────────────────

describe('BpmnCanvas — overlay de automatización', () => {
  it('sin actividades automatizables: overlays.add NO se llama, remove sí', async () => {
    await act(async () => {
      render(<BpmnCanvas xml={SIMPLE_XML} automatableElementIds={[]} />)
    })
    await flush()

    expect(mockOverlaysAdd).not.toHaveBeenCalled()
    expect(mockOverlaysRemove).toHaveBeenCalledWith({ type: 'automation-indicator' })
  })

  it('posición: usa elementRegistry para calcular left=width-12, top=-8 (esquina superior derecha)', async () => {
    await act(async () => {
      render(<BpmnCanvas xml={SIMPLE_XML} automatableElementIds={['task_a']} />)
    })
    await flush()

    expect(mockOverlaysAdd).toHaveBeenCalledOnce()
    const position = mockOverlaysAdd.mock.calls[0][2].position

    // top: -8 — badge 8px por encima del borde superior del nodo
    expect(position.top).toBe(EXPECTED_TOP)
    // left: width-12 = 88 — badge en la región derecha, 8px fuera del borde derecho (20-12=8px sticking out)
    expect(position.left).toBe(EXPECTED_LEFT)
    // No usa right (semántica confusa en diagram-js: right:-8 → left=8+width, enteramente fuera)
    expect(position.right).toBeUndefined()
  })

  it('con 2 actividades automatizables: overlays.add llamado 2 veces con posición correcta', async () => {
    await act(async () => {
      render(<BpmnCanvas xml={SIMPLE_XML} automatableElementIds={['task_a', 'task_b']} />)
    })
    await flush()

    expect(mockOverlaysAdd).toHaveBeenCalledTimes(2)
    for (const call of mockOverlaysAdd.mock.calls) {
      expect(call[2].position).toEqual({ top: EXPECTED_TOP, left: EXPECTED_LEFT })
    }
  })

  it('HTML del overlay: title correcto, SVG ámbar (#f59e0b), fondo #fffbeb', async () => {
    await act(async () => {
      render(<BpmnCanvas xml={SIMPLE_XML} automatableElementIds={['task_1']} />)
    })
    await flush()

    const html: string = mockOverlaysAdd.mock.calls[0][2].html

    expect(html).toContain('Actividad marcada como automatizable')
    expect(html).toContain('<svg')
    expect(html).toContain('#f59e0b')   // color ámbar del ícono Bot
    expect(html).toContain('#fffbeb')   // fondo amber-50 del círculo
  })

  it('al quitar una actividad: remove() vuelve a llamarse y add() refleja la lista nueva', async () => {
    const { rerender } = render(
      <BpmnCanvas xml={SIMPLE_XML} automatableElementIds={['task_a', 'task_b']} />
    )
    await flush()

    expect(mockOverlaysAdd).toHaveBeenCalledTimes(2)
    const removeCalls1 = mockOverlaysRemove.mock.calls.length

    await act(async () => {
      rerender(<BpmnCanvas xml={SIMPLE_XML} automatableElementIds={['task_a']} />)
    })
    await flush()

    expect(mockOverlaysRemove.mock.calls.length).toBeGreaterThan(removeCalls1)
    expect(mockOverlaysAdd).toHaveBeenCalledTimes(3) // 2 + 1
  })
})
