<script setup>
import { ref, reactive, onMounted } from 'vue'
import MQTTService from '../../services/mqtt.js'

import Accordion from 'primevue/accordion';
import AccordionPanel from 'primevue/accordionpanel';
import AccordionHeader from 'primevue/accordionheader';
import AccordionContent from 'primevue/accordioncontent';
import Dialog from 'primevue/dialog'

import { JsonForms } from "@jsonforms/vue";
import { createAjv } from '@jsonforms/core'
import { primeVueRenderers } from 'jsonforms-primevue'

import { useToast } from 'primevue/usetoast'

const toast = useToast()

const renderers = Object.freeze([
  ...primeVueRenderers,
  // here you can add custom renderers
]);

const props = defineProps({
  deviceId: String
})

const mqtt2 = new MQTTService()

const filters = ref({})
const commands = ref([])
const commandMap = reactive({})
const commandByResponseId = reactive({})

const defaultsAjv = createAjv({ useDefaults: true })
// Suppress Ajv unknown format warning by registering a no-op validator for "textarea"
defaultsAjv.addFormat('textarea', true)

function rebuildList() {
  commands.value = Object.values(commandMap)
}

function updateCommand(device, name, field, value) {
  if (!commandMap[name]) {
        commandMap[name] = {
        name,
        description: "No description provided.",
        schema: {},
        input: {},
        response: null
        }
  }

  commandMap[name][field] = value
  rebuildList()
}

function sendCommand(cmd) {
  cmd.responseId = null
  cmd.response = {}

  const topic = `device/${props.deviceId}/command/${cmd.name}`
  var payload = JSON.stringify(cmd.input, null, 2)

  var responseId = generateId()
  commandByResponseId[responseId] = cmd

  mqtt2.publish(topic, payload || '{}', {
    qos: 1,
    properties: {
      responseTopic: `client/${mqtt2.clientId}/responses`,
      correlationData: new TextEncoder().encode(responseId)
    }
  })
}

function generateId() {
  return Math.random().toString(16).substr(2, 8)
}

const schemaDialog = reactive({
  visible: false,
  content: ''
})

function showSchema(cmd) {
  try {
    schemaDialog.content = JSON.stringify(cmd.schema, null, 2)
  } catch {
    schemaDialog.content = cmd.schema || 'No schema available'
  }
  schemaDialog.visible = true
}

onMounted(() => {

  mqtt2.subscribe('device/+/command/#', (payload, topic) => {

    const parts = topic.split('/')

    const device = parts[1]
    if (device !== props.deviceId) return

    const command = parts[3]
    const field = parts[4]

    if (field === '$description') {
      updateCommand(device, command, 'description', payload)
    }
    if (field === '$schema') {
        const schema = JSON.parse(payload)
      updateCommand(device, command, 'schema', schema)
    }

  })

  mqtt2.subscribe(`client/${mqtt2.clientId}/responses`, (payload, topic) => {
    let response = JSON.parse(payload)
    
    const responseId = response.correlation
    const cmd = commandByResponseId[responseId]

    if (cmd) {
      cmd.response = response
      
      toast.add({
        severity: response.success ? 'success' : 'error',
        summary: cmd.name,
        detail: response.message !== 'None' ? response.message : (response.success ? 'Success' : 'Failed'),
        life: 4000
      })
      
      delete commandByResponseId[responseId]
    }
  })
})

const jsonFormsConfig = {
    validationMode: 'ValidateOnTouched',
    showAllErrors: false,
    showErrorsOnTouched: true,
}

</script>

<template>
    <Dialog
        v-model:visible="schemaDialog.visible"
        header="Command Schema"
        :modal="true"
        :closable="true"
        :style="{width: '700px'}"
        >
        <pre>{{ schemaDialog.content }}</pre>
    </Dialog>
  <div class="layout-card col-item-2">
    <DataView :value="commands">
        <template #header>
            <div class="flex products-header">
                <span class="products-title">Commands</span>
                <IconField class="search-field">
                    <InputIcon class="pi pi-search" />
                    <InputText v-model="filters['global']" placeholder="Filter by..." />
                </IconField>
            </div>
        </template>
      <template #empty> No commands available.</template>
      <template #list="slotProps">
        <Accordion >

            <AccordionPanel
            v-for="cmd in slotProps.items"
            :key="cmd.name"
            :value="cmd.name"
            >

            <AccordionHeader>
                <div class="command-header">
                <span class="command-name">{{ cmd.name }}</span>
                <span class="command-desc">{{ cmd.description }}</span>
                </div>
            </AccordionHeader>
            <AccordionContent>
                <div class="command-content">
                <!-- Left: Code editor -->
                <JsonForms
                    :data="cmd.input"
                    :schema="cmd.schema"
                    :renderers="renderers"
                    :config="jsonFormsConfig"
                    :onChange="({ data, errors }) => cmd.input = data"
                    :ajv="defaultsAjv"
                />

                <!-- Right: Buttons -->
                <div class="button-container">
                <Button
                  v-if="cmd.response"
                  :icon="cmd.response.success ? 'pi pi-check' : 'pi pi-times'"
                  class="p-button-rounded p-button-text"
                  :class="cmd.response.success ? 'p-button-success' : 'p-button-danger'"
                  v-tooltip="cmd.response.message !== 'None' ? cmd.response.message : null"
                />
                <Button
                    icon="pi pi-question-circle"
                    class="p-mb-2"
                    label="Help"
                    severity="secondary"
                    @click="showSchema(cmd)"
                    tooltip="Show schema"
                />
                <Button
                    icon="pi pi-send"
                    label="Send"
                    @click="sendCommand(cmd)"
                    tooltip="Send command"
                />
                </div>
                </div>
            </AccordionContent>

            </AccordionPanel>

        </Accordion>
        </template>
    </DataView>
</div>
</template>

<style scoped>

.command-header {
  display: flex;
  gap: 10px;
  align-items: center;
}

.command-name {
  font-weight: 600;
}

.command-desc {
  font-size: 0.8rem;
}


.command-content {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}

.editor-container {
  flex: 1;
}

.button-container {
  display: flex;
  flex-direction: row;
  gap: 0.5rem;
  justify-content: flex-end;
}

</style>