<template>
  <div class="task-node" :style="{ marginLeft: depth * 20 + 'px' }">
    <div class="task-row" :class="['task-' + (job.status || 'open'), { 'task-expanded': isExpanded }]"
      @click="isExpanded = !isExpanded">
      <div class="row items-center no-wrap q-gutter-x-sm">
        <!-- Expand/collapse for nodes with children -->
        <q-btn v-if="children.length" flat round dense size="xs"
          :icon="treeExpanded ? 'expand_more' : 'chevron_right'" color="grey-6"
          @click.stop="treeExpanded = !treeExpanded" style="margin:-4px" />
        <div v-else style="width:24px" />

        <q-icon :name="statusIcon" :color="statusColor" size="18px" />
        <div class="col" style="min-width:0">
          <div class="row items-center no-wrap q-gutter-x-xs">
            <code class="task-id">{{ job.name }}</code>
            <span v-if="job.step_order" class="task-step-badge">{{ job.step_order }}</span>
            <span class="text-weight-medium ellipsis-text" style="font-size:0.85rem">{{ job.subject }}</span>
          </div>
          <div class="text-caption text-grey-6 row items-center q-gutter-x-sm">
            <span v-if="job.job_type">{{ job.job_type }}</span>
            <span v-if="job.assigned_tech" class="text-primary"><q-icon name="person" size="12px" /> {{ job.assigned_tech }}</span>
            <span v-if="job.scheduled_date"><q-icon name="event" size="12px" /> {{ job.scheduled_date }}</span>
            <span v-if="job.depends_on" class="text-warning">
              <q-icon name="link" size="12px" /> après {{ job.depends_on }}
            </span>
            <span v-if="job.on_open_webhook || job.on_close_webhook" class="text-info">
              <q-icon name="webhook" size="12px" /> n8n
            </span>
            <span v-if="children.length" class="text-grey-5">
              <q-icon name="account_tree" size="12px" /> {{ children.length }}
            </span>
            <!-- Tag chips inline -->
            <span v-for="tag in jobTags" :key="tag" class="task-tag-chip">{{ tag }}</span>
          </div>
        </div>
        <span class="ops-badge" :class="badgeClass" style="font-size:10px;padding:1px 6px;flex-shrink:0">
          {{ job.status || 'open' }}
        </span>
        <q-icon name="expand_more" size="16px" color="grey-4"
          :style="{ transform: isExpanded ? 'rotate(180deg)' : 'rotate(0)', transition: 'transform 0.2s' }" />
      </div>
    </div>

    <!-- ═══ Expanded edit panel ═══ -->
    <div v-if="isExpanded" class="task-edit-panel" @click.stop>
      <div class="row q-col-gutter-sm q-mb-sm">
        <div class="col-12">
          <q-input v-model="editSubject" dense outlined label="Sujet"
            :input-style="{ fontSize: '0.8rem' }" @blur="saveField('subject', editSubject)"
            @keydown.enter="saveField('subject', editSubject)" />
        </div>
      </div>
      <div class="row q-col-gutter-sm q-mb-sm">
        <div class="col-4">
          <q-select v-model="editStatus" dense outlined emit-value map-options label="Statut"
            :options="statusOptions" @update:model-value="saveField('status', $event)" />
        </div>
        <div class="col-4">
          <q-select v-model="editPriority" dense outlined emit-value map-options label="Priorité"
            :options="priorityOptions" @update:model-value="saveField('priority', $event)" />
        </div>
        <div class="col-4">
          <q-select v-model="editJobType" dense outlined emit-value map-options label="Type"
            :options="jobTypeOptions" @update:model-value="saveField('job_type', $event)" />
        </div>
      </div>
      <div class="row q-col-gutter-sm q-mb-sm">
        <div class="col-6">
          <q-select v-model="editTech" dense outlined emit-value map-options label="Technicien"
            :options="techOptions" clearable @update:model-value="assignTech"
            :loading="loadingTechs" @focus="loadTechsOnce">
            <template #prepend><q-icon name="person" size="16px" /></template>
          </q-select>
        </div>
        <div class="col-3">
          <q-input v-model="editDate" dense outlined type="date" label="Date"
            @update:model-value="saveField('scheduled_date', $event || '')" />
        </div>
        <div class="col-3">
          <q-input v-model.number="editDuration" dense outlined type="number" label="Durée (h)"
            @blur="saveField('duration_h', editDuration)" />
        </div>
      </div>

      <!-- Tags row -->
      <div class="q-mb-sm">
        <div class="text-caption text-grey-6 q-mb-xs" style="font-weight:600">
          <q-icon name="sell" size="14px" /> Tags (pour dispatch timeline)
        </div>
        <div class="task-tags-row">
          <q-chip v-for="tag in jobTags" :key="tag" removable dense size="sm"
            color="green-1" text-color="green-8" icon="sell"
            @remove="removeTag(tag)">
            {{ tag }}
          </q-chip>
          <q-select v-model="newTag" dense outlined emit-value map-options
            :options="availableTagOptions" label="+ Tag" style="min-width:140px;max-width:200px"
            clearable @update:model-value="addTag" @focus="loadTagsOnce"
            :loading="loadingTags" :input-style="{ fontSize: '0.75rem' }" />
        </div>
      </div>

      <!-- Action buttons -->
      <div class="row items-center q-gutter-x-sm">
        <q-btn flat dense size="sm" icon="open_in_new" label="Dispatch" color="primary" no-caps
          @click="goToDispatch">
          <q-tooltip>Ouvrir dans le tableau dispatch</q-tooltip>
        </q-btn>
        <q-space />
        <q-btn flat dense size="sm" icon="delete" label="Supprimer" color="negative" no-caps
          @click="confirmDelete">
          <q-tooltip>Supprimer cette tâche</q-tooltip>
        </q-btn>
      </div>
    </div>

    <!-- Children (recursive) -->
    <template v-if="treeExpanded && children.length">
      <TaskNode
        v-for="child in children" :key="child.name"
        :job="child" :all-jobs="allJobs" :depth="depth + 1"
        @fire-webhook="(url, j) => $emit('fire-webhook', url, j)"
        @deleted="(name) => $emit('deleted', name)"
        @updated="(name, data) => $emit('updated', name, data)"
      />
    </template>
  </div>
</template>

<script setup>
import { ref, computed, watch } from 'vue'
import { Notify, Dialog } from 'quasar'
import { updateJob } from 'src/api/dispatch'
import { deleteDoc, listDocs } from 'src/api/erp'
import { fetchTags, fetchTechnicians } from 'src/api/dispatch'
import { HUB_URL } from 'src/config/hub'
import { useRouter } from 'vue-router'

const props = defineProps({
  job: { type: Object, required: true },
  allJobs: { type: Array, default: () => [] },
  depth: { type: Number, default: 0 },
})

const emit = defineEmits(['fire-webhook', 'deleted', 'updated'])
const router = useRouter()

const isExpanded = ref(false)
const treeExpanded = ref(true)

// ── Edit state (synced from job prop) ──
const editSubject = ref(props.job.subject || '')
const editStatus = ref(props.job.status || 'open')
const editPriority = ref(props.job.priority || 'medium')
const editJobType = ref(props.job.job_type || 'Autre')
const editTech = ref(props.job.assigned_tech || null)
const editDate = ref(props.job.scheduled_date || '')
const editDuration = ref(props.job.duration_h || 1)

// Sync when job prop changes
watch(() => props.job, (j) => {
  editSubject.value = j.subject || ''
  editStatus.value = j.status || 'open'
  editPriority.value = j.priority || 'medium'
  editJobType.value = j.job_type || 'Autre'
  editTech.value = j.assigned_tech || null
  editDate.value = j.scheduled_date || ''
  editDuration.value = j.duration_h || 1
}, { deep: true })

// ── Options ──
const statusOptions = [
  { label: 'Ouvert', value: 'open' },
  { label: 'Assigné', value: 'assigned' },
  { label: 'En route', value: 'in-route' },
  { label: 'En cours', value: 'in-progress' },
  { label: 'Complété', value: 'completed' },
  { label: 'Annulé', value: 'cancelled' },
]
// Priorité Dispatch Job — SOURCE UNIQUE (config/dispatch-priority).
import { DISPATCH_PRIORITIES as priorityOptions } from 'src/config/dispatch-priority'
// Types de job — SOURCE UNIQUE (config/job-types).
import { JOB_TYPE_OPTIONS as jobTypeOptions } from 'src/config/job-types'

// ── Tech loading (lazy) ──
const techOptions = ref([])
const loadingTechs = ref(false)
let techsLoaded = false

async function loadTechsOnce () {
  if (techsLoaded) return
  loadingTechs.value = true
  try {
    const techs = await fetchTechnicians()
    techOptions.value = techs.map(t => ({
      label: t.full_name || t.name,
      value: t.technician_id || t.name,
    }))
    techsLoaded = true
  } catch { /* best effort */ }
  loadingTechs.value = false
}

// ── Tags (lazy load, light-themed) ──
const allTagsList = ref([])
const loadingTags = ref(false)
let tagsLoaded = false
const newTag = ref(null)

const jobTags = computed(() => {
  // Parse tags from the job — they might be in job.tags child table
  if (Array.isArray(props.job.tags)) {
    return props.job.tags.map(t => typeof t === 'string' ? t : t.tag).filter(Boolean)
  }
  return []
})

const availableTagOptions = computed(() => {
  const current = new Set(jobTags.value)
  return allTagsList.value
    .filter(t => !current.has(t.label) && !current.has(t.name))
    .map(t => ({ label: t.label || t.name, value: t.label || t.name }))
})

async function loadTagsOnce () {
  if (tagsLoaded) return
  loadingTags.value = true
  try {
    allTagsList.value = await fetchTags()
    tagsLoaded = true
  } catch { /* best effort */ }
  loadingTags.value = false
}

function addTag (tagLabel) {
  if (!tagLabel) return
  const currentTags = jobTags.value.map(t => ({ tag: t }))
  currentTags.push({ tag: tagLabel })
  newTag.value = null
  updateJob(props.job.name, { tags: currentTags }).then(() => {
    // Update local state
    if (!props.job.tags) props.job.tags = []
    props.job.tags.push({ tag: tagLabel })
    emit('updated', props.job.name, { tags: props.job.tags })
  }).catch(() => Notify.create({ type: 'negative', message: 'Erreur ajout tag' }))
}

function removeTag (tagLabel) {
  const newTags = jobTags.value.filter(t => t !== tagLabel).map(t => ({ tag: t }))
  updateJob(props.job.name, { tags: newTags }).then(() => {
    props.job.tags = newTags
    emit('updated', props.job.name, { tags: newTags })
  }).catch(() => Notify.create({ type: 'negative', message: 'Erreur suppression tag' }))
}

// ── Save field ──
async function saveField (field, value) {
  try {
    const payload = { [field]: value }
    // If status changes, also fire webhook
    if (field === 'status') {
      const prev = props.job.status
      props.job.status = value
      if (value === 'completed' && props.job.on_close_webhook) {
        emit('fire-webhook', props.job.on_close_webhook, props.job)
      } else if (value === 'assigned' && prev === 'open' && props.job.on_open_webhook) {
        emit('fire-webhook', props.job.on_open_webhook, props.job)
      }
    }
    await updateJob(props.job.name, payload)
    // Update local
    props.job[field] = value
    emit('updated', props.job.name, payload)
  } catch (err) {
    Notify.create({ type: 'negative', message: `Erreur: ${err.message || err}` })
  }
}

// ── Assign tech ──
async function assignTech (techId) {
  try {
    const payload = { assigned_tech: techId || '' }
    if (techId && editStatus.value === 'open') {
      payload.status = 'assigned'
      editStatus.value = 'assigned'
    } else if (!techId && editStatus.value === 'assigned') {
      payload.status = 'open'
      editStatus.value = 'open'
    }
    await updateJob(props.job.name, payload)
    props.job.assigned_tech = techId || null
    if (payload.status) props.job.status = payload.status
    emit('updated', props.job.name, payload)
    if (techId) {
      Notify.create({ type: 'info', message: `Assigné à ${techId}`, timeout: 2000 })
    }
  } catch (err) {
    Notify.create({ type: 'negative', message: `Erreur: ${err.message || err}` })
  }
}

// ── Delete ──
// Use the targo-hub /dispatch/job-delete endpoint instead of the raw ERPNext
// DELETE. The endpoint rewires any child job's `depends_on` (and re-parents
// descendants if the victim was a chain root) before deleting, so the chain
// stays intact and ERPNext's LinkExistsError doesn't fire.
function confirmDelete () {
  // Warn the user when the task has dependents — the chain will be rewired
  // (successors move up to the victim's parent) rather than broken.
  const children = props.allJobs.filter(j => j.depends_on === props.job.name)
  const warning = children.length
    ? ` <br><span style="color:#f59e0b;font-size:0.85em;">⚠ ${children.length} étape(s) dépendent de celle-ci — elles seront reliées à l'étape précédente.</span>`
    : ''
  Dialog.create({
    title: 'Supprimer cette tâche ?',
    message: `${props.job.name}: ${props.job.subject}${warning}`,
    html: true,
    cancel: { flat: true, label: 'Annuler' },
    ok: { color: 'red', label: 'Supprimer', unelevated: true },
    persistent: true,
  }).onOk(async () => {
    try {
      // NB: no `credentials: 'include'` — the hub responds with
      // `Access-Control-Allow-Origin: *`, and that combination is forbidden
      // by the CORS spec (browsers reject the preflight as NetworkError).
      // Every other hub call in this SPA follows the same pattern.
      const res = await fetch(`${HUB_URL}/dispatch/job-delete`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ job: props.job.name }),
      })
      const body = await res.json().catch(() => ({}))
      if (!res.ok) throw new Error(body.error || `HTTP ${res.status}`)
      const msg = body.rewired && body.rewired.length
        ? `Tâche supprimée (${body.rewired.length} dépendance${body.rewired.length > 1 ? 's' : ''} recâblée${body.rewired.length > 1 ? 's' : ''})`
        : 'Tâche supprimée'
      Notify.create({ type: 'positive', message: msg, timeout: 2500 })
      emit('deleted', props.job.name)
    } catch (err) {
      Notify.create({ type: 'negative', message: `Erreur: ${err.message || err}` })
    }
  })
}

// ── Navigate to planification (ex-Dispatch, page retirée) ──
function goToDispatch () {
  router.push('/planification')
}

// ── Visual computed ──
const children = computed(() =>
  props.allJobs
    .filter(j => j.parent_job === props.job.name)
    .sort((a, b) => (a.step_order || 0) - (b.step_order || 0))
)

const statusIcon = computed(() => {
  const s = (props.job.status || '').toLowerCase()
  if (s === 'completed') return 'check_circle'
  if (s === 'assigned') return 'person'
  if (s === 'in progress' || s === 'in-progress') return 'play_circle'
  if (s === 'in-route') return 'directions_car'
  if (s === 'cancelled') return 'cancel'
  return 'radio_button_unchecked'
})

const statusColor = computed(() => {
  const s = (props.job.status || '').toLowerCase()
  if (s === 'completed') return 'green-6'
  if (s === 'assigned') return 'blue-6'
  if (s === 'in progress' || s === 'in-progress') return 'orange-6'
  if (s === 'in-route') return 'amber-8'
  if (s === 'cancelled') return 'grey-5'
  return 'grey-5'
})

const badgeClass = computed(() => {
  const s = (props.job.status || '').toLowerCase()
  if (s === 'completed') return 'active'
  if (s === 'assigned') return 'draft'
  if (s === 'in progress' || s === 'in-progress') return 'open'
  if (s === 'cancelled') return 'inactive'
  return 'closed'
})
</script>

<style scoped>
.task-node { transition: margin 0.15s; }
.task-row {
  background: #fff;
  border: 1px solid #e2e8f0;
  border-radius: 6px;
  padding: 6px 10px;
  transition: all 0.15s;
  cursor: pointer;
}
.task-row:hover { border-color: #6366f1; background: #fafafe; }
.task-row.task-expanded { border-color: #6366f1; border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.task-row.task-completed { border-left: 3px solid #10b981; }
.task-row.task-assigned { border-left: 3px solid #6366f1; }
.task-row.task-open { border-left: 3px solid #94a3b8; }
.task-row.task-cancelled { border-left: 3px solid #ef4444; opacity: 0.6; }
.task-row.task-in-progress { border-left: 3px solid #f59e0b; }
.task-row.task-in-route { border-left: 3px solid #f97316; }

.task-edit-panel {
  background: #f8fafc;
  border: 1px solid #6366f1;
  border-top: none;
  border-radius: 0 0 6px 6px;
  padding: 10px 12px;
  animation: slideDown 0.15s ease;
}
@keyframes slideDown {
  from { opacity: 0; max-height: 0; }
  to { opacity: 1; max-height: 500px; }
}

.task-id {
  font-size: 0.65rem;
  color: #6366f1;
  background: #eef2ff;
  padding: 1px 4px;
  border-radius: 3px;
  white-space: nowrap;
}
.task-step-badge {
  font-size: 0.6rem;
  font-weight: 700;
  color: #fff;
  background: #6366f1;
  width: 16px; height: 16px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.task-tag-chip {
  font-size: 0.6rem;
  font-weight: 600;
  color: #4338ca;
  background: #e0e7ff;
  padding: 0 5px;
  border-radius: 8px;
  white-space: nowrap;
}
.task-tags-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
}
.ellipsis-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
</style>
