// P8.2d (KAR-987 §24 "E2E") — Spec c: Multi-Touch/isPrimary.
//
// Closes the THIRD P8.2b jsdom-Lückenliste item: "Echtes Multi-Touch/Pinch
// in jsdom nicht simulierbar — isPrimary/activePointerIdRef-Logik nur durch
// synthetische Einzel-Pointer-Events mit isPrimary:false getestet." This
// drives TWO REAL, CONCURRENT touch contacts via CDP (see
// helpers/touch.ts's own doc comment for why Playwright's high-level
// `page.touchscreen` cannot do this) — Chromium itself decides which one is
// `isPrimary`, not a hand-set flag on a fabricated event.
//
// Flag state: `wertstromUxV2Enabled: false` (default) — the pointer/drag
// input layer this spec exercises is unconditional (Baustein 1, P8.2b).
import { test, expect } from '@playwright/experimental-ct-react'
import VsmEditor from '@/components/wertstrom/vsm-editor'
import { buildEditorProps } from './fixtures/vsm-fixtures'
import { nodeByName, canvasSurface } from './helpers/locators'
import { collectConsoleErrors } from './helpers/console-errors'
import { createRealTouch } from './helpers/touch'

test.use({ hasTouch: true })

test('zweiter Touch-Pointer stoert die laufende Geste des ersten nicht', async ({ mount, page }) => {
  const consoleErrors = collectConsoleErrors(page)
  const component = await mount(<VsmEditor {...buildEditorProps()} />)
  const nodeA = nodeByName(component, 'Prozess A')
  const canvas = canvasSurface(component)

  const aBox = await nodeA.boundingBox()
  if (!aBox) throw new Error('Prozess A node has no bounding box')
  const canvasBox = await canvas.boundingBox()
  if (!canvasBox) throw new Error('canvas surface has no bounding box')

  // Coordinates are tracked explicitly in variables (not re-derived from
  // bounding boxes between steps) so "move finger 1 by 60px" always means
  // exactly that, regardless of where the node's box currently sits.
  let t1x = aBox.x + aBox.width / 2
  const t1y = aBox.y + aBox.height / 2
  // Second finger's landing spot: bottom-right corner of the canvas, well
  // clear of both fixture nodes (both sit at the same, higher-up y).
  const t2x = canvasBox.x + canvasBox.width - 40
  const t2y = canvasBox.y + canvasBox.height - 40

  const touch = await createRealTouch(page)

  // Finger 1 down on Prozess A — the only contact so far, so Chromium marks
  // it isPrimary:true (handleNodeMouseDown's `if (!e.isPrimary) return`
  // guard lets it through, same as a real single-finger drag would).
  await touch.start([{ id: 0, x: t1x, y: t1y }])
  t1x += 60
  await touch.move([{ id: 0, x: t1x, y: t1y }])

  const afterFirstMove = await nodeA.boundingBox()
  if (!afterFirstMove) throw new Error('Prozess A node lost its bounding box')
  expect(afterFirstMove.x - aBox.x).toBeCloseTo(60, 0)

  // Finger 2 touches down on EMPTY canvas WHILE finger 1 is still active —
  // this is now a genuinely concurrent second contact, so Chromium marks it
  // isPrimary:false. Both vsm-editor.tsx guards this could hit
  // (handleNodeMouseDown for a node, handleCanvasMouseDown for empty canvas
  // — this lands on the canvas) start with `if (!e.isPrimary) return`, so
  // this must neither start a competing pan-drag (which would shift EVERY
  // node's rendered position, A included) nor touch A's still-running drag.
  await touch.start([
    { id: 0, x: t1x, y: t1y },
    { id: 1, x: t2x, y: t2y },
  ])

  // Move ONLY the second finger.
  await touch.move([
    { id: 0, x: t1x, y: t1y },
    { id: 1, x: t2x - 80, y: t2y - 80 },
  ])
  const duringSecondTouch = await nodeA.boundingBox()
  if (!duringSecondTouch) throw new Error('Prozess A node lost its bounding box')
  expect(duringSecondTouch.x).toBeCloseTo(afterFirstMove.x, 0)
  expect(duringSecondTouch.y).toBeCloseTo(afterFirstMove.y, 0)

  // Finger 1 keeps moving WHILE finger 2 is still down — its own gesture
  // (activePointerIdRef pinned to finger 1's pointerId) must still be fully
  // alive, unaffected by the second, non-primary contact's presence.
  t1x += 40
  await touch.move([
    { id: 0, x: t1x, y: t1y },
    { id: 1, x: t2x - 80, y: t2y - 80 },
  ])
  const afterContinuedFirst = await nodeA.boundingBox()
  if (!afterContinuedFirst) throw new Error('Prozess A node lost its bounding box')
  expect(afterContinuedFirst.x - afterFirstMove.x).toBeCloseTo(40, 0)

  // Lift finger 2 — `end()`'s `points` are the one(s) BEING RELEASED (see
  // helpers/touch.ts's own doc comment: empirically the OPPOSITE convention
  // from start/move), so this lists id:1 at its last position, not id:0.
  // Finger 1's gesture must remain completely unaffected.
  await touch.end([{ id: 1, x: t2x - 80, y: t2y - 80 }])
  const afterSecondLift = await nodeA.boundingBox()
  if (!afterSecondLift) throw new Error('Prozess A node lost its bounding box')
  expect(afterSecondLift.x).toBeCloseTo(afterContinuedFirst.x, 0)

  // Finger 1 continues once more, alone, then lifts — a clean, ordinary end.
  t1x += 20
  await touch.move([{ id: 0, x: t1x, y: t1y }])
  const afterFinalMove = await nodeA.boundingBox()
  if (!afterFinalMove) throw new Error('Prozess A node lost its bounding box')
  expect(afterFinalMove.x - afterSecondLift.x).toBeCloseTo(20, 0)

  await touch.end([{ id: 0, x: t1x, y: t1y }])

  expect(consoleErrors).toEqual([])
})
