import { expect, test, type Page } from '@playwright/test'

import { expectDefined } from '#tests/helpers/assert'
import { CanvasHelper } from '#tests/helpers/canvas'

async function selectedNodeSnapshot(page: Page) {
  return page.evaluate(() => {
    const store = window.openPencil?.getStore?.()
    if (!store) throw new Error('OpenPencil store not initialized')
    const selectedId = [...store.state.selectedIds][0]
    const selected = selectedId ? store.graph.getNode(selectedId) : null
    return selected
      ? {
          id: selected.id,
          type: selected.type,
          parentId: selected.parentId,
          componentId: selected.componentId,
          pageId: store.state.currentPageId,
          width: selected.width,
          childTexts: store.graph
            .getChildren(selected.id)
            .filter((child) => child.type === 'TEXT')
            .map((child) => child.text)
        }
      : null
  })
}

test('assets panel groups component sets and inserts the default variant', async ({ page }) => {
  const canvas = new CanvasHelper(page)
  await page.goto('/?test')
  await canvas.waitForInit()

  const ids = await page.evaluate(() => {
    const store = window.openPencil?.getStore?.()
    if (!store) throw new Error('OpenPencil store not initialized')

    const pageNode = store.graph.getNode(store.state.currentPageId)
    if (!pageNode) throw new Error('Current page not found')

    const set = store.graph.createNode('COMPONENT_SET', pageNode.id, {
      name: 'Button',
      x: 80,
      y: 80,
      width: 260,
      height: 100,
      sourceLibraryKey: 'lk-test-library',
      symbolDescription: 'Reusable button component',
      symbolLinks: [{ uri: 'https://example.com/button', displayName: 'Button docs' }],
      componentPropertyDefinitions: [
        {
          id: 'prop:type',
          name: 'Type',
          type: 'VARIANT',
          defaultValue: 'Secondary',
          variantOptions: ['Primary', 'Secondary']
        }
      ]
    })
    const primary = store.graph.createNode('COMPONENT', set.id, {
      name: 'Type=Primary',
      x: 0,
      y: 0,
      width: 96,
      height: 40,
      componentPropertyValues: { Type: 'Primary' }
    })
    store.graph.createNode('TEXT', primary.id, {
      name: 'Label',
      text: 'Primary',
      width: 72,
      height: 20
    })
    const secondary = store.graph.createNode('COMPONENT', set.id, {
      name: 'Type=Secondary',
      x: 120,
      y: 0,
      width: 132,
      height: 40,
      componentPropertyValues: { Type: 'Secondary' }
    })
    store.graph.createNode('TEXT', secondary.id, {
      name: 'Label',
      text: 'Secondary',
      width: 96,
      height: 20
    })
    const duplicateSecondary = store.graph.createNode('COMPONENT', set.id, {
      name: 'Type=Secondary duplicate',
      x: 280,
      y: 0,
      width: 132,
      height: 40,
      componentPropertyValues: { Type: 'Secondary' }
    })
    const card = store.graph.createNode('COMPONENT', pageNode.id, {
      name: 'Card',
      x: 80,
      y: 240,
      width: 160,
      height: 100
    })
    store.requestRender()
    return {
      setId: set.id,
      primaryId: primary.id,
      secondaryId: secondary.id,
      duplicateSecondaryId: duplicateSecondary.id,
      cardId: card.id
    }
  })
  await canvas.waitForRender()

  await page.getByTestId('left-panel-assets-tab').click()

  const assetsPanel = page.getByTestId('assets-panel')
  const assetItems = page.getByTestId('asset-item')
  await expect(assetItems).toHaveCount(2)
  await expect(assetsPanel).toContainText('Button')
  await expect(assetsPanel).toContainText('Card')
  await expect(assetsPanel).not.toContainText('Type=Primary')
  await expect(assetsPanel).not.toContainText('Type=Secondary')
  const buttonAsset = page.locator(`[data-asset-id="${ids.setId}"]`)
  await expect(buttonAsset.getByTestId('asset-variant-summary')).toContainText('Type')
  await expect(buttonAsset.getByTestId('asset-library-badge')).toContainText('Library')
  await expect(buttonAsset.getByTestId('asset-description')).toContainText(
    'Reusable button component'
  )
  await expect(buttonAsset.getByTestId('asset-docs')).toBeVisible()
  await expect(buttonAsset.getByTestId('asset-variant-conflict')).toContainText(
    'Duplicate variant values'
  )

  await buttonAsset.click()
  const details = page.getByTestId('asset-details-dialog')
  await expect(details).toBeVisible()
  await expect(details).toContainText('Button')
  await expect(details.getByTestId('asset-details-preview')).toBeVisible()
  await expect(details.getByTestId('asset-details-preview-image')).toBeVisible()
  await expect(details.getByTestId('asset-details-description')).toContainText(
    'Reusable button component'
  )
  await expect(details.getByTestId('asset-details-library')).toContainText('lk-test-library')
  await expect(details.getByTestId('asset-details-docs')).toBeVisible()
  await expect(details.getByTestId('asset-details-property')).toContainText('Type')
  await page.getByTestId('asset-details-close').click()
  await expect(details).toBeHidden()

  await page.getByTestId('assets-search').fill('card')
  await expect(assetItems).toHaveCount(1)
  await expect(assetsPanel).toContainText('Card')
  await page.locator(`[data-asset-id="${ids.cardId}"]`).getByTestId('asset-insert').click()
  await canvas.waitForRender()

  const cardInstance = await selectedNodeSnapshot(page)
  expect(cardInstance?.type).toBe('INSTANCE')
  expect(cardInstance?.componentId).toBe(ids.cardId)

  await page.getByTestId('assets-search').fill('missing asset')
  await expect(page.getByTestId('assets-empty')).toBeVisible()
  await expect(assetItems).toHaveCount(0)

  await page.getByTestId('assets-search').fill('button')
  await expect(assetItems).toHaveCount(1)
  await page.locator(`[data-asset-id="${ids.setId}"]`).getByTestId('asset-insert').click()
  await canvas.waitForRender()

  const inserted = await selectedNodeSnapshot(page)

  expect(inserted?.type).toBe('INSTANCE')
  expect(inserted?.componentId).toBe(ids.secondaryId)
  expect(inserted?.parentId).toBe(inserted?.pageId)
  expect(inserted?.width).toBe(132)
  expect(inserted?.childTexts).toEqual(['Secondary'])

  await expect(page.getByTestId('variant-section')).toBeVisible()

  await page.getByTestId('variant-section').getByTestId('app-select-trigger').click()
  await page.getByRole('option', { name: 'Primary' }).click()

  expectDefined(inserted?.id, 'inserted instance id')
  const switched = await selectedNodeSnapshot(page)
  expect(switched?.componentId).toBe(ids.primaryId)
  expect(switched?.width).toBe(96)
  expect(switched?.childTexts).toEqual(['Primary'])

  canvas.assertNoErrors()
})

test('assets insertion accounts for entered container coordinates', async ({ page }) => {
  const canvas = new CanvasHelper(page)
  await page.goto('/?test')
  await canvas.waitForInit()

  const setup = await page.evaluate(() => {
    const store = window.openPencil?.getStore?.()
    if (!store) throw new Error('OpenPencil store not initialized')
    store.state.panX = 0
    store.state.panY = 0
    store.state.zoom = 1
    const pageNode = store.graph.getNode(store.state.currentPageId)
    if (!pageNode) throw new Error('Current page not found')
    const frame = store.graph.createNode('FRAME', pageNode.id, {
      name: 'Target Frame',
      x: 200,
      y: 150,
      width: 300,
      height: 240
    })
    const component = store.graph.createNode('COMPONENT', pageNode.id, {
      name: 'Panel Card',
      x: 40,
      y: 40,
      width: 120,
      height: 60
    })
    store.state.enteredContainerId = frame.id
    store.requestRender()
    return { frameId: frame.id, componentId: component.id }
  })
  await canvas.waitForRender()

  await page.getByTestId('left-panel-assets-tab').click()
  await page.locator(`[data-asset-id="${setup.componentId}"]`).getByTestId('asset-insert').click()
  await canvas.waitForRender()

  const inserted = await page.evaluate(() => {
    const store = window.openPencil?.getStore?.()
    if (!store) throw new Error('OpenPencil store not initialized')
    const selectedId = [...store.state.selectedIds][0]
    const selected = selectedId ? store.graph.getNode(selectedId) : null
    if (!selected) return null
    const abs = store.graph.getAbsolutePosition(selected.id)
    const center = store.screenToCanvas(
      ...(Object.values(store.viewportCanvasCenter()) as [number, number])
    )
    return {
      parentId: selected.parentId,
      centerX: abs.x + selected.width / 2,
      centerY: abs.y + selected.height / 2,
      expectedCenterX: center.x,
      expectedCenterY: center.y
    }
  })

  expect(inserted?.parentId).toBe(setup.frameId)
  expect(inserted?.centerX).toBeCloseTo(inserted?.expectedCenterX ?? 0, 1)
  expect(inserted?.centerY).toBeCloseTo(inserted?.expectedCenterY ?? 0, 1)
  canvas.assertNoErrors()
})

test('demo exposes component set assets', async ({ page }) => {
  const canvas = new CanvasHelper(page)
  await page.goto('/demo')
  await canvas.waitForInit()

  await page.getByTestId('left-panel-assets-tab').click()
  const assetsPanel = page.getByTestId('assets-panel')

  await expect(assetsPanel).toContainText('Button')
  await expect(assetsPanel).toContainText('2 variants · Variant')
  await expect(assetsPanel).toContainText('Avatar')
  await expect(assetsPanel).not.toContainText('Button/Primary')

  canvas.assertNoErrors()
})
