import test from 'node:test'
import assert from 'node:assert/strict'
import { existsSync, readFileSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
import { dirname, resolve } from 'node:path'

import {
  addBlockInstance,
  businessBlockCanvas,
  parseBusinessBlockLayout,
  reorderBlockInstance,
  updateBlockConfig,
  validateRequiredConfig
} from './business-blocks/blockLayout.js'
import {
  buildDefaultChartInstance,
  updateChartDataset,
  updateChartDisplay,
  validateChartInstance
} from './business-blocks/chartBlock.js'

const currentDir = dirname(fileURLToPath(import.meta.url))

function readSource(relativePath) {
  return readFileSync(resolve(currentDir, relativePath), 'utf8')
}

const cartDefinition = {
  code: 'cart',
  name: 'Cart',
  configs: [
    { key: 'cartTable', label: 'Cart table', type: 'table', required: true },
    { key: 'productName', label: 'Product name field', type: 'field', table: 'productTable', required: true }
  ]
}

const lineDefinition = {
  code: 'admin_line_chart',
  name: 'Line chart',
  kind: 'chart',
  chartType: 'line',
  datasetSchema: 'single-table-aggregate-v1',
  defaultSpan: 12
}

test('business block helpers create an empty business block canvas', () => {
  const layout = parseBusinessBlockLayout('')

  assert.equal(layout.canvas, businessBlockCanvas)
  assert.deepEqual(layout.blocks, [])
})

test('business block helpers add a block instance with stable generated id', () => {
  const layout = addBlockInstance(parseBusinessBlockLayout(''), cartDefinition, () => 'cart_001')

  assert.equal(layout.blocks[0].id, 'cart_001')
  assert.equal(layout.blocks[0].blockCode, 'cart')
  assert.deepEqual(layout.blocks[0].config, {})
})

test('business block helpers update block config without replacing other blocks', () => {
  let layout = addBlockInstance(parseBusinessBlockLayout(''), cartDefinition, () => 'cart_001')
  layout = addBlockInstance(layout, { ...cartDefinition, code: 'notice', name: 'Notice' }, () => 'notice_001')

  layout = updateBlockConfig(layout, 'cart_001', { cartTable: 'shop_cart' })

  assert.equal(layout.blocks[0].config.cartTable, 'shop_cart')
  assert.deepEqual(layout.blocks[1].config, {})
})

test('business block helpers reorder block instances', () => {
  let layout = addBlockInstance(parseBusinessBlockLayout(''), cartDefinition, () => 'cart_001')
  layout = addBlockInstance(layout, { ...cartDefinition, code: 'notice', name: 'Notice' }, () => 'notice_001')

  layout = reorderBlockInstance(layout, 1, 0)

  assert.equal(layout.blocks[0].id, 'notice_001')
  assert.equal(layout.blocks[1].id, 'cart_001')
})

test('business block helpers report missing required config labels', () => {
  const missing = validateRequiredConfig(cartDefinition, { cartTable: 'shop_cart' })

  assert.deepEqual(missing, ['Product name field'])
})

test('chart helper builds a deterministic line chart instance', () => {
  const block = buildDefaultChartInstance(lineDefinition, 'line_001')

  assert.equal(block.span, 12)
  assert.equal(block.dataset.schema, 'single-table-aggregate-v1')
  assert.deepEqual(block.dataset.metrics, [
    { field: '', aggregate: 'sum', alias: 'metric1', label: '指标 1' }
  ])
  assert.equal(block.display.showLegend, true)
})

test('chart helper updates dataset and display immutably', () => {
  const block = buildDefaultChartInstance(lineDefinition, 'line_001')
  const dataset = { ...block.dataset, table: 'shop_order' }
  const display = { ...block.display, smooth: false }

  const withDataset = updateChartDataset(block, dataset)
  const withDisplay = updateChartDisplay(withDataset, display)

  assert.equal(block.dataset.table, '')
  assert.equal(withDataset.dataset.table, 'shop_order')
  assert.equal(withDisplay.display.smooth, false)
})

test('chart helper reports a missing dimension for line charts', () => {
  const block = buildDefaultChartInstance(lineDefinition, 'line_001')
  const errors = validateChartInstance(lineDefinition, block, [])

  assert.equal(errors.some((item) => item.key === 'dimension.field'), true)
})

test('business block designer is composed from focused subcomponents', () => {
  assert.equal(existsSync(resolve(currentDir, 'business-blocks/BusinessBlockDesigner.vue')), true)
  assert.equal(existsSync(resolve(currentDir, 'business-blocks/BlockPalette.vue')), true)
  assert.equal(existsSync(resolve(currentDir, 'business-blocks/BlockCanvas.vue')), true)
  assert.equal(existsSync(resolve(currentDir, 'business-blocks/BlockConfigForm.vue')), true)

  const source = readSource('business-blocks/BusinessBlockDesigner.vue')
  assert.equal(source.includes("import BlockPalette from './BlockPalette.vue'"), true)
  assert.equal(source.includes("import BlockCanvas from './BlockCanvas.vue'"), true)
  assert.equal(source.includes("import BlockConfigForm from './BlockConfigForm.vue'"), true)
  assert.equal(source.includes('update:layout-json'), true)
  assert.equal(source.includes('select-block'), true)
  assert.equal(source.includes('showConfig'), true)
  assert.equal(source.includes('showPalette'), true)
  assert.equal(source.includes('v-if="showPalette"'), true)
  assert.equal(source.includes('v-if="showConfig"'), true)
  assert.equal(source.includes('defineExpose'), true)
  assert.equal(source.includes('<BlockConfigForm'), true)
})

test('business block schema form renders table and field controls', () => {
  const source = readSource('business-blocks/BlockConfigForm.vue')

  assert.equal(source.includes("field.type === 'table'"), true)
  assert.equal(source.includes("field.type === 'field'"), true)
  assert.equal(source.includes('<el-select'), true)
  assert.equal(source.includes('update:modelValue'), true)
})

test('chart blocks use a dedicated dataset and display editor', () => {
  const source = readSource('business-blocks/BusinessBlockDesigner.vue')
  const formPath = resolve(currentDir, 'business-blocks/ChartBlockConfigForm.vue')
  const filterPath = resolve(currentDir, 'business-blocks/ChartFilterEditor.vue')

  assert.equal(existsSync(formPath), true)
  assert.equal(existsSync(filterPath), true)

  const form = readSource('business-blocks/ChartBlockConfigForm.vue')
  const filters = readSource('business-blocks/ChartFilterEditor.vue')
  assert.equal(source.includes("import ChartBlockConfigForm from './ChartBlockConfigForm.vue'"), true)
  assert.equal(source.includes("selectedDefinition?.kind === 'chart'"), true)
  assert.equal(source.includes('selectedChartErrors'), true)
  assert.equal(form.includes('update:dataset'), true)
  assert.equal(form.includes('update:display'), true)
  assert.equal(form.includes('update:span'), true)
  assert.equal(form.includes('<ChartFilterEditor'), true)
  assert.equal(filters.includes("'eq'"), true)
  assert.equal(filters.includes("'between'"), true)
  assert.equal(filters.includes("'is_not_null'"), true)
})

test('business block canvas supports drag reorder and block removal', () => {
  const source = readSource('business-blocks/BlockCanvas.vue')

  assert.equal(source.includes('draggable="true"'), true)
  assert.equal(source.includes('reorder-block'), true)
  assert.equal(source.includes('remove-block'), true)
})

test('chart canvas renders previews in a twelve-column grid', () => {
  const canvas = readSource('business-blocks/BlockCanvas.vue')
  const previewPath = resolve(currentDir, 'business-blocks/ChartBlockPreview.vue')

  assert.equal(existsSync(previewPath), true)
  const preview = readSource('business-blocks/ChartBlockPreview.vue')
  assert.equal(canvas.includes('grid-template-columns: repeat(12, minmax(0, 1fr))'), true)
  assert.equal(canvas.includes(':style="{ gridColumn: `span ${block.span || 12}` }"'), true)
  assert.equal(canvas.includes('<ChartBlockPreview'), true)
  assert.equal(canvas.includes('role="button"'), true)
  assert.equal(preview.includes("import * as echarts from 'echarts'"), true)
  assert.equal(preview.includes('chart.resize()'), true)
  assert.equal(preview.includes('chart.dispose()'), true)
})
