/**
 * Tests de la sección "Automatización" del ActivityPanel.
 *
 * Los mocks del store usan vi.hoisted() para crear referencias estables:
 * si el mock devuelve una nueva referencia de activities en cada render,
 * el useEffect de ActivityPanel entra en loop infinito.
 */

import { describe, it, expect, vi, beforeEach } from 'vitest'
import { render, screen, fireEvent, act } from '@testing-library/react'
import { TooltipProvider } from '@/components/ui/tooltip'

// ─── Mocks con referencias estables vía vi.hoisted ───────────────────────────

const processStoreMock = vi.hoisted(() => {
  const activity = {
    id: 'act-1', processId: 'p1', bpmnElementId: 'task1', name: 'Facturación',
    type: 'task' as const,
    directCostFixed: 200, executionTimeMinutes: 60, assignedResources: [],
    automatable: false, automatedCostFixed: 0, automatedTimeMinutes: 0,
  }
  return {
    activities: [activity],   // referencia estable — no cambia entre renders
    resources: [],
    updateActivity: vi.fn(),
    subscribe: vi.fn(),        // requerido por simulation-store.ts al importarse
  }
})

const simulationStoreMock = vi.hoisted(() => ({
  resultActual: null as null | object,
  resultAutomated: null as null | object,
  invalidateResults: vi.fn(),
}))

vi.mock('@/store/process-store', () => ({
  useProcessStore: () => processStoreMock,
  // Zustand también expone .subscribe() como método estático del hook
  useProcessStore_subscribe: vi.fn(),
}))

vi.mock('@/store/simulation-store', () => ({
  useSimulationStore: () => simulationStoreMock,
}))

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

// ─── Helper ───────────────────────────────────────────────────────────────────

function renderPanel(selectedElementId = 'task1') {
  return render(
    <TooltipProvider>
      <ActivityPanel selectedElementId={selectedElementId} />
    </TooltipProvider>
  )
}

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

beforeEach(() => {
  // Resetear el mock de updateActivity y restaurar la actividad base
  processStoreMock.updateActivity.mockReset()
  processStoreMock.updateActivity.mockResolvedValue(undefined)
  processStoreMock.activities[0].automatable = false
  processStoreMock.activities[0].automatedCostFixed = 0
  processStoreMock.activities[0].automatedTimeMinutes = 0
  simulationStoreMock.resultActual = null
  simulationStoreMock.resultAutomated = null
  simulationStoreMock.invalidateResults.mockReset()
})

describe('ActivityPanel — sección Automatización', () => {
  it('sección Automatización está presente y toggle en OFF por defecto', () => {
    renderPanel()
    expect(screen.getByText('¿Es automatizable?')).toBeInTheDocument()
    const toggle = screen.getByRole('switch', { name: /automatizable/i })
    expect(toggle).toHaveAttribute('aria-checked', 'false')
  })

  it('Toggle ON: aria-checked cambia a true', async () => {
    renderPanel()
    const toggle = screen.getByRole('switch', { name: /automatizable/i })

    await act(async () => { fireEvent.click(toggle) })

    expect(toggle).toHaveAttribute('aria-checked', 'true')
  })

  it('Toggle ON con ambos campos en 0: helper ámbar está en el DOM y visible (opacity-100)', async () => {
    renderPanel()
    const toggle = screen.getByRole('switch', { name: /automatizable/i })

    await act(async () => { fireEvent.click(toggle) })

    // El helper siempre está en el DOM — verificamos que el texto existe y el contenedor es visible
    const helperText = screen.getByText(/Transparencia metodológica/i)
    expect(helperText).toBeInTheDocument()
    // El contenedor padre tiene opacity-100 cuando showHelper=true
    const helperContainer = helperText.closest('[aria-hidden]')
    expect(helperContainer).toHaveAttribute('aria-hidden', 'false')
  })

  it('Toggle ON con costo > 0: helper ámbar está en el DOM pero oculto (aria-hidden=true)', async () => {
    renderPanel()
    const toggle = screen.getByRole('switch', { name: /automatizable/i })
    await act(async () => { fireEvent.click(toggle) })

    const costoInput = screen.getByLabelText(/Costo automatizado/i)
    await act(async () => {
      fireEvent.change(costoInput, { target: { value: '50' } })
    })

    // El texto sigue en el DOM (transición CSS, no unmount)
    const helperText = screen.getByText(/Transparencia metodológica/i)
    expect(helperText).toBeInTheDocument()
    // Pero el contenedor está marcado aria-hidden=true (oculto visualmente)
    const helperContainer = helperText.closest('[aria-hidden]')
    expect(helperContainer).toHaveAttribute('aria-hidden', 'true')
  })

  it('Editar costo automatizado y guardar llama updateActivity con el valor correcto', async () => {
    renderPanel()
    const toggle = screen.getByRole('switch', { name: /automatizable/i })
    await act(async () => { fireEvent.click(toggle) })

    const costoInput = screen.getByLabelText(/Costo automatizado/i)
    await act(async () => {
      fireEvent.change(costoInput, { target: { value: '25' } })
    })

    const guardarBtn = screen.getByRole('button', { name: /Guardar/i })
    await act(async () => { fireEvent.click(guardarBtn) })

    expect(processStoreMock.updateActivity).toHaveBeenCalledOnce()
    const arg = processStoreMock.updateActivity.mock.calls[0][0]
    expect(arg.automatedCostFixed).toBe(25)
    expect(arg.automatable).toBe(true)
  })

  it('Guardar llama updateActivity (la invalidación reactiva es responsabilidad del store)', async () => {
    renderPanel()
    const toggle = screen.getByRole('switch', { name: /automatizable/i })
    await act(async () => { fireEvent.click(toggle) })

    const tiempoInput = screen.getByLabelText(/Tiempo automatizado/i)
    await act(async () => {
      fireEvent.change(tiempoInput, { target: { value: '5' } })
    })

    const guardarBtn = screen.getByRole('button', { name: /Guardar/i })
    await act(async () => { fireEvent.click(guardarBtn) })

    expect(processStoreMock.updateActivity).toHaveBeenCalledOnce()
    const arg = processStoreMock.updateActivity.mock.calls[0][0]
    expect(arg.automatedTimeMinutes).toBe(5)
  })
})
