<!--
  StepEditorModal.vue — Modal form for editing a single step.

  Renders kind-specific fields dynamically from the kind catalog, so adding a
  new step kind = adding an entry in kind-catalogs.js (no edit here needed).

  Props:
    - modelValue:  open/closed boolean (v-model)
    - step:        the step being edited (cloned internally to avoid mutation)
    - kindCatalog: PROJECT_KINDS | AGENT_KINDS
    - allStepIds:  array of step IDs available as depends_on targets
    - appliesTo:   domain context (e.g. 'Customer'), used by the variable picker

  Events:
    - update:modelValue(boolean)
    - save(step):  user clicked save, receives the edited step

  Performance:
    - Deep-clones the step on open (O(payload size)) to isolate edits.
    - Field rendering driven by a computed descriptor list — one render cycle
      per kind change.
-->
<template>
  <q-dialog :model-value="modelValue" @update:model-value="$emit('update:modelValue', $event)"
    persistent maximized-bp="sm">
    <q-card class="step-editor-card" style="width: 560px; max-width: 95vw">
      <q-card-section class="step-editor-hdr">
        <div class="row items-center">
          <q-icon :name="kindDef.icon" size="20px" :style="{ color: kindDef.color }" class="q-mr-sm" />
          <div class="col">
            <div class="text-subtitle1 text-weight-bold">Modifier l'étape</div>
            <div class="text-caption text-grey-7">{{ kindDef.label }}</div>
          </div>
          <!-- Click a variable → `{{path}}` is copied to the clipboard so the
               user can paste it into any field below (SMS body, email subject,
               webhook URL, …). No direct-inject because we'd need a ref on
               every FieldInput + cursor tracking, which isn't worth it. -->
          <VariablePicker :applies-to="appliesTo" mode="copy" label="Variables" />
          <q-btn flat round dense icon="close" @click="close" class="q-ml-xs" />
        </div>
      </q-card-section>

      <q-card-section class="step-editor-body" style="max-height: 70vh; overflow-y: auto">
        <!-- Core fields — always present -->
        <div class="row q-col-gutter-sm q-mb-md">
          <div class="col-12">
            <q-input v-model="local.label" dense outlined label="Nom de l'étape" stack-label />
          </div>
        </div>

        <div class="row q-col-gutter-sm q-mb-md">
          <div class="col-6">
            <q-select v-model="local.kind" dense outlined emit-value map-options
              :options="kindOptions" label="Type d'étape" stack-label
              @update:model-value="onKindChange" />
          </div>
          <div class="col-6">
            <q-select v-model="triggerType" dense outlined emit-value map-options
              :options="triggerOptions" label="Déclencheur" stack-label
              @update:model-value="onTriggerChange" />
          </div>
        </div>

        <!-- Trigger-specific fields -->
        <div v-if="triggerDef.fields.length" class="row q-col-gutter-sm q-mb-md">
          <div v-for="f in triggerDef.fields" :key="f.name" class="col-6">
            <FieldInput :field="f" :model-value="local.trigger[f.name]"
              @update:model-value="val => (local.trigger[f.name] = val)" />
          </div>
        </div>

        <!-- Dependencies — options are {label, value} so chips show the step
             name (e.g. "Retrait ancien site") while the stored value stays
             the stable id ("s2"). Prevents the "← s4" confusion where the
             user couldn't tell which step was referenced. -->
        <div class="q-mb-md">
          <q-select v-model="local.depends_on" dense outlined multiple use-chips
            emit-value map-options :options="dependsOnOptions"
            label="Dépend de" stack-label
            hint="Cette étape attend que les étapes listées soient complétées" />
        </div>

        <!-- Kind-specific payload fields -->
        <div v-if="kindDef.fields.length" class="step-editor-section">
          <div class="text-caption text-weight-bold text-grey-8 q-mb-sm">
            Paramètres — {{ kindDef.label }}
          </div>
          <div class="row q-col-gutter-sm">
            <div v-for="f in kindDef.fields" :key="f.name" :class="fieldWidth(f)">
              <FieldInput :field="f" :model-value="local.payload[f.name]"
                @update:model-value="val => (local.payload[f.name] = val)" />
            </div>
          </div>
        </div>

        <!-- Parent branch (shown if the step has a parent condition) -->
        <div v-if="local.parent_id" class="q-mt-md">
          <q-input v-model="local.branch" dense outlined label="Branche parent"
            stack-label hint="yes / no / custom" />
        </div>
      </q-card-section>

      <q-card-actions class="step-editor-ftr">
        <q-btn flat color="grey-7" label="Annuler" @click="close" no-caps />
        <q-space />
        <q-btn unelevated color="primary" label="Enregistrer" @click="save" no-caps
          icon="check" />
      </q-card-actions>
    </q-card>
  </q-dialog>
</template>

<script setup>
import { ref, computed, watch } from 'vue'
import { getKind, getTrigger, TRIGGER_TYPES } from './kind-catalogs'
import FieldInput from './FieldInput.vue'
import VariablePicker from './VariablePicker.vue'

const props = defineProps({
  modelValue:  { type: Boolean, default: false },
  step:        { type: Object,  default: null },
  kindCatalog: { type: Object,  required: true },
  allStepIds:  { type: Array,   default: () => [] },
  // Full step list (id + label) so we can render depends_on chips with
  // human-readable labels instead of opaque "s4" IDs. Fallback:
  // allStepIds is still honored when allSteps isn't passed.
  allSteps:    { type: Array,   default: () => [] },
  appliesTo:   { type: String,  default: null },
})

const emit = defineEmits(['update:modelValue', 'save'])

/** Local deep clone — mutations don't affect parent until user hits save. */
const local = ref(cloneStep(props.step))
const triggerType = ref(local.value.trigger?.type || 'on_prev_complete')

// Reset local state when the modal reopens on a different step
watch(() => [props.modelValue, props.step?.id], () => {
  if (props.modelValue && props.step) {
    local.value = cloneStep(props.step)
    triggerType.value = local.value.trigger?.type || 'on_prev_complete'
  }
})

/** Deep-clone a step so edits are isolated until save. */
function cloneStep (step) {
  if (!step) return { id: '', kind: 'notify', label: '', payload: {}, trigger: { type: 'on_prev_complete' }, depends_on: [] }
  return JSON.parse(JSON.stringify(step))
}

/** Kind descriptor from the catalog. */
const kindDef = computed(() => getKind(props.kindCatalog, local.value.kind))

/** Trigger descriptor from the shared trigger map. */
const triggerDef = computed(() => getTrigger(triggerType.value))

/** Options for the "type d'étape" select (derived once from the catalog). */
const kindOptions = computed(() =>
  Object.entries(props.kindCatalog).map(([key, def]) => ({
    label: def.label, value: key,
  }))
)

/** Options for the "trigger" select. */
const triggerOptions = computed(() =>
  Object.entries(TRIGGER_TYPES).map(([key, def]) => ({
    label: def.label, value: key,
  }))
)

/** depends_on options — exclude self to prevent circular refs. */
const otherStepIds = computed(() =>
  props.allStepIds.filter(id => id !== local.value.id)
)

/**
 * depends_on dropdown options with human-readable labels.
 *
 * Shape: `{ label: "Retrait ancien site", value: "s2" }[]`.
 *
 * Falls back to `allStepIds` when the caller didn't pass `allSteps` — that
 * way the control never breaks, it just degrades to raw ids like before.
 */
const dependsOnOptions = computed(() => {
  if (props.allSteps?.length) {
    return props.allSteps
      .filter(s => s.id !== local.value.id)
      .map(s => ({ label: s.label || s.id, value: s.id }))
  }
  return otherStepIds.value.map(id => ({ label: id, value: id }))
})

/** Handle kind change: reset payload defaults, preserve id/label. */
function onKindChange (newKind) {
  const def = getKind(props.kindCatalog, newKind)
  const payload = {}
  for (const f of def.fields || []) {
    if (f.default !== undefined) payload[f.name] = f.default
  }
  local.value.payload = payload
}

/** Handle trigger change: reset trigger-specific fields. */
function onTriggerChange (newType) {
  local.value.trigger = { type: newType }
}

/** Heuristic: textarea fields span full width, others are half. */
function fieldWidth (field) {
  if (field.type === 'textarea' || field.type === 'json') return 'col-12'
  return 'col-12 col-md-6'
}

function save () {
  // Normalize empty arrays / nulls
  if (!Array.isArray(local.value.depends_on)) local.value.depends_on = []
  emit('save', JSON.parse(JSON.stringify(local.value)))
  emit('update:modelValue', false)
}

function close () {
  emit('update:modelValue', false)
}
</script>

<style scoped>
.step-editor-card { border-radius: 8px; }
.step-editor-hdr {
  border-bottom: 1px solid #e2e8f0;
  padding: 12px 16px;
}
.step-editor-body { padding: 16px; }
.step-editor-section {
  border-top: 1px solid #f1f5f9;
  padding-top: 12px;
  margin-top: 8px;
}
.step-editor-ftr {
  border-top: 1px solid #e2e8f0;
  padding: 10px 16px;
}
</style>
