<template>
  <section class="business-block-designer" :class="{ 'with-config': showConfig, 'without-palette': !showPalette }">
    <BlockPalette v-if="showPalette" :definitions="blockDefinitions" @add-block="addBlock" />
    <BlockCanvas
      :blocks="layout.blocks"
      :definitions="blockDefinitions"
      :selected-id="selectedBlockId"
      @select-block="selectBlock"
      @reorder-block="reorderBlock"
      @remove-block="removeBlock"
    />
    <template v-if="showConfig">
      <ChartBlockConfigForm
        v-if="selectedDefinition?.kind === 'chart'"
        :definition="selectedDefinition"
        :dataset="selectedBlock?.dataset || {}"
        :display="selectedBlock?.display || {}"
        :span="selectedBlock?.span || 12"
        :tables="tables"
        :errors="selectedChartErrors"
        @update:dataset="updateSelectedDataset"
        @update:display="updateSelectedDisplay"
        @update:span="updateSelectedSpan"
      />
      <BlockConfigForm
        v-else
        :definition="selectedDefinition"
        :model-value="selectedBlock?.config || {}"
        :tables="tables"
        @update:model-value="updateSelectedConfig"
      />
    </template>
  </section>
</template>

<script setup>
import { computed, ref, watch } from 'vue'
import BlockPalette from './BlockPalette.vue'
import BlockCanvas from './BlockCanvas.vue'
import BlockConfigForm from './BlockConfigForm.vue'
import ChartBlockConfigForm from './ChartBlockConfigForm.vue'
import { validateChartInstance } from './chartBlock.js'
import {
  addBlockInstance,
  parseBusinessBlockLayout,
  removeBlockInstance,
  reorderBlockInstance,
  updateBlockConfig
} from './blockLayout.js'

const props = defineProps({
  layoutJson: { type: String, default: '' },
  blockDefinitions: { type: Array, default: () => [] },
  tables: { type: Array, default: () => [] },
  showConfig: { type: Boolean, default: true },
  showPalette: { type: Boolean, default: true }
})

const emit = defineEmits(['update:layout-json', 'select-block'])
const layout = ref(parseBusinessBlockLayout(props.layoutJson))
const selectedBlockId = ref('')

const selectedBlock = computed(() => layout.value.blocks.find((block) => block.id === selectedBlockId.value) || null)
const selectedDefinition = computed(() => props.blockDefinitions.find((item) => item.code === selectedBlock.value?.blockCode) || null)
const selectedChartErrors = computed(() => (
  selectedDefinition.value?.kind === 'chart'
    ? validateChartInstance(selectedDefinition.value, selectedBlock.value, props.tables)
    : []
))

watch(
  () => props.layoutJson,
  (value) => {
    layout.value = parseBusinessBlockLayout(value)
    if (!layout.value.blocks.some((block) => block.id === selectedBlockId.value)) {
      selectedBlockId.value = layout.value.blocks[0]?.id || ''
    }
  },
  { immediate: true }
)

watch(
  () => ({
    block: selectedBlock.value,
    definition: selectedDefinition.value
  }),
  (payload) => {
    emit('select-block', payload)
  },
  { immediate: true }
)

function addBlock(definition) {
  layout.value = addBlockInstance(layout.value, definition, nextBlockId)
  selectedBlockId.value = layout.value.blocks[layout.value.blocks.length - 1]?.id || ''
  emitLayout()
}

function selectBlock(blockId) {
  selectedBlockId.value = blockId || ''
}

function updateSelectedConfig(config) {
  if (!selectedBlock.value) return
  layout.value = updateBlockConfig(layout.value, selectedBlock.value.id, config)
  emitLayout()
}

function replaceSelectedBlock(patch) {
  if (!selectedBlock.value) return
  layout.value = {
    ...layout.value,
    blocks: layout.value.blocks.map((block) => (
      block.id === selectedBlock.value.id ? { ...block, ...patch } : block
    ))
  }
  emitLayout()
}

function updateSelectedDataset(dataset) {
  replaceSelectedBlock({ dataset })
}

function updateSelectedDisplay(display) {
  replaceSelectedBlock({ display })
}

function updateSelectedSpan(span) {
  replaceSelectedBlock({ span })
}

function reorderBlock(payload) {
  layout.value = reorderBlockInstance(layout.value, payload.fromIndex, payload.toIndex)
  emitLayout()
}

function removeBlock(blockId) {
  layout.value = removeBlockInstance(layout.value, blockId)
  selectedBlockId.value = layout.value.blocks[0]?.id || ''
  emitLayout()
}

function nextBlockId(code) {
  const count = layout.value.blocks.filter((block) => block.blockCode === code).length + 1
  return `${code}_${String(count).padStart(3, '0')}`
}

function emitLayout() {
  emit('update:layout-json', JSON.stringify(layout.value, null, 2))
}

defineExpose({ addBlock })
</script>

<style scoped lang="scss">
.business-block-designer {
  display: grid;
  grid-template-columns: minmax(220px, 280px) minmax(420px, 1fr);
  align-items: start;
  gap: 16px;
}

.business-block-designer.with-config {
  grid-template-columns: minmax(190px, 240px) minmax(360px, 1fr) minmax(320px, 380px);
}

.business-block-designer.without-palette {
  grid-template-columns: minmax(360px, 1fr);
}

.business-block-designer.with-config.without-palette {
  grid-template-columns: minmax(360px, 1fr) minmax(320px, 380px);
}

@media (max-width: 1080px) {
  .business-block-designer {
    grid-template-columns: 1fr;
  }
}
</style>
