<script setup lang="ts">
/**
 * « /decider — Le chemin » : une phrase → un chemin exécutable en un tap.
 * Le moteur pur recalcule à chaque geste ; tout est expliqué en une phrase,
 * tout est contournable (alourdir libre, alléger motivé). Chemin nominal :
 * phrase + Entrée (au Fil) puis LE bouton — 2 gestes.
 */
import type { ApplyPathEdits } from '~/stores/decisions'
import type {
  ChainKind, Decision, DecisionRoute, Id, ParamSpec, Reversibility,
  TriageInput, Verdict, Weight,
} from '~/types/domain'
import { computeConcerned } from '~/engine'
import {
  ADOPTED_STAMP, ADOPTED_TOAST, METHOD_LABELS,
  CAPTURE_PLACEHOLDER, ROUTE_LABELS, WHY_DISCLOSURE,
} from '~/lexicon'
import { useCollectiveStore } from '~/stores/collective'
import { useDecisionsStore } from '~/stores/decisions'

type AltKey = 'parametric' | 'record' | 'binary'

const col = useCollectiveStore()
const decisions = useDecisionsStore()
const route = useRoute()

// ── State of the tunnel ──
const title = ref('')
const scope = ref<Decision['scope']>({ selfOnly: true, circleIds: [], personIds: [] })
const reversibility = ref<Reversibility>('easy')
const weight = ref<Weight>('light')
const urgent = ref(false)
const amendsClauseId = ref<Id | undefined>()
const parentId = ref<Id | undefined>()
const chainKind = ref<ChainKind | undefined>()
const baselineNote = ref('')
const resourceNote = ref('')
const resourceAmount = ref<number | null>(null)
const resourceUnit = ref('heures')
const chosenAlt = ref<AltKey | null>(null)
const decidedHow = ref('')
const overrideNote = ref('')
const spec = ref<ParamSpec | undefined>()
const reviewChecked = ref(false)
const engraveChecked = ref(false)
const mandateAttached = ref(false)
const error = ref('')
const stamping = ref(false)

// ── Prefill from the query (?titre ?clause ?parent ?chain ?mandat) ──
const q = route.query
if (typeof q.titre === 'string') title.value = q.titre
if (typeof q.clause === 'string') amendsClauseId.value = q.clause
if (typeof q.parent === 'string') {
  const prefill = decisions.reopen(q.parent)
  if (!('ok' in prefill)) {
    parentId.value = prefill.parentDecisionId
    chainKind.value = prefill.chainKind
    if (title.value.length === 0) title.value = prefill.title
    scope.value = prefill.scope
    reversibility.value = prefill.reversibility
    weight.value = prefill.weight
    if (prefill.amendsClauseId) amendsClauseId.value = prefill.amendsClauseId
  }
}
if (typeof q.chain === 'string') chainKind.value = q.chain as ChainKind
if (typeof q.mandat === 'string') {
  const mandate = col.mandates.find(m => m.id === q.mandat)
  if (mandate) {
    scope.value = { selfOnly: false, circleIds: [...mandate.domain.circleIds], personIds: [] }
  }
}

// ── The live path — the pure engine recomputes at every gesture ──
const TAG_RE = /#([\p{L}\p{N}-]+)/gu
const tags = computed(() => [...title.value.matchAll(TAG_RE)].map(m => m[1]!.toLowerCase()))

const engineInput = computed<TriageInput>(() => ({
  title: title.value,
  tags: tags.value,
  scope: scope.value,
  reversibility: reversibility.value,
  weight: weight.value,
  urgent: urgent.value,
  ...(amendsClauseId.value ? { amendsClauseId: amendsClauseId.value } : {}),
}))
const chemin = computed<Verdict | null>(() => {
  const res = decisions.runTriage(engineInput.value)
  return 'ok' in res ? null : res
})

watch(() => chemin.value?.reviewRequired, v => { reviewChecked.value = v === true }, { immediate: true })
watch(() => chemin.value?.engravingSuggested, v => { engraveChecked.value = v === true }, { immediate: true })

// ── Concernés pré-calculés (plancher) ──
const stack = computed(() => {
  if (scope.value.selfOnly || !col.me) return []
  const active = col.mandates.filter(m => m.status === 'active')
  return computeConcerned(scope.value, col.circles, active, col.me.id).flatMap((entry) => {
    const person = col.people.find(p => p.id === entry.personId)
    return person ? [{ person, origin: 'computed' as const, reason: entry.reason }] : []
  })
})

// ── Route effective & asymétrie de dérogation ──
const ROUTE_ORDER: Record<DecisionRoute, number> = {
  record: 0, solo: 0, mandate: 1, transmit: 1, advice: 2, collective: 3,
}
const effectiveRoute = computed<DecisionRoute>(() => {
  if (!chemin.value) return 'solo'
  if (!chosenAlt.value) return chemin.value.route
  return chosenAlt.value === 'record' ? 'record' : 'collective'
})
const overridden = computed(() => chemin.value !== null && effectiveRoute.value !== chemin.value.route)
const lightening = computed(
  () => chemin.value !== null && ROUTE_ORDER[effectiveRoute.value] < ROUTE_ORDER[chemin.value.route],
)
const overrideLabel = computed(() => {
  if (chosenAlt.value === 'parametric') return METHOD_LABELS.parametric
  if (chosenAlt.value === 'binary') return METHOD_LABELS.binary
  if (chosenAlt.value === 'record') return ROUTE_LABELS.record
  return ''
})

// ── Frise : durée / seuil (si connus) ──
const protocol = computed(() => {
  if (effectiveRoute.value !== 'collective') return undefined
  let id = chemin.value?.protocolId
  if (chosenAlt.value === 'parametric') id = col.settings?.protocolByRange.parametric ?? id
  if (chosenAlt.value === 'binary') id = col.protocols.find(p => p.method === 'binary')?.id ?? id
  return col.protocols.find(p => p.id === id)
})
const framingPlanned = computed(
  () => chemin.value?.framingDays !== undefined && effectiveRoute.value === 'collective',
)
const durationLabel = computed(() => {
  const v = chemin.value
  if (!v || effectiveRoute.value === 'solo' || effectiveRoute.value === 'record') return ''
  if (framingPlanned.value) return `${v.framingDays} jours de formulation`
  if (v.windowHours !== undefined && effectiveRoute.value === v.route) return `fenêtre de ${v.windowHours} h`
  return protocol.value ? `${protocol.value.durationDays} jours de vote` : ''
})
const thresholdLabel = computed(() => {
  const p = protocol.value
  if (!p) return ''
  if (p.method === 'consent') return 'zéro objection maintenue'
  return METHOD_LABELS[p.method]
})

// ── Bouton principal ──
const mainLabel = computed(() => {
  switch (effectiveRoute.value) {
    case 'advice': return 'Demande leur avis'
    case 'transmit': return 'Transmets'
    case 'record': return 'Consigne'
    case 'collective': return framingPlanned.value ? 'Ouvre la formulation' : 'Ouvre le vote'
    default: return 'Décide'
  }
})
const blockReason = computed(() =>
  lightening.value && overrideNote.value.trim().length === 0
    ? 'Alléger se motive — écris pourquoi, ta note s\'affichera.'
    : '',
)
const disabled = computed(
  () => title.value.trim().length === 0 || blockReason.value !== '' || chemin.value === null,
)

const engagesOpen = computed(
  () => (weight.value === 'binding' || weight.value === 'structural') && !scope.value.selfOnly,
)
const baselineSuggested = computed(() => ['advice', 'collective'].includes(effectiveRoute.value))
const suggestion = computed(() => chemin.value?.suggestion)
const reviewLocked = computed(
  () => chemin.value?.reviewRequired === true && effectiveRoute.value !== 'solo',
)

// ── Valider : capture → applyPath → fiche (ou Fil si adopté sur-le-champ) ──
async function validate() {
  const v = chemin.value
  if (!v || disabled.value) return
  const created = decisions.capture(title.value)
  if ('ok' in created) { error.value = created.reason; return }
  if (parentId.value) {
    created.parentDecisionId = parentId.value
    created.chainKind = chainKind.value ?? 'revision'
  }
  const edits: ApplyPathEdits = {
    scope: {
      selfOnly: scope.value.selfOnly,
      circleIds: [...scope.value.circleIds],
      personIds: [...scope.value.personIds],
    },
    reversibility: reversibility.value,
    weight: weight.value,
    urgent: urgent.value,
    visibility: scope.value.selfOnly ? 'private' : 'scope',
  }
  if (effectiveRoute.value !== v.route) edits.route = effectiveRoute.value
  if (amendsClauseId.value) edits.amendsClauseId = amendsClauseId.value
  if (baselineSuggested.value && baselineNote.value.trim()) edits.baselineNote = baselineNote.value.trim()
  if (resourceNote.value.trim().length > 0 || typeof resourceAmount.value === 'number') {
    edits.resources = {
      note: resourceNote.value.trim(),
      ...(typeof resourceAmount.value === 'number'
        ? { amount: resourceAmount.value, unit: resourceUnit.value }
        : {}),
    }
  }
  if (chosenAlt.value === 'record' && decidedHow.value.trim()) edits.decidedHow = decidedHow.value.trim()
  if (chosenAlt.value === 'parametric') {
    if (spec.value) edits.paramSpec = spec.value
    const pid = col.settings?.protocolByRange.parametric
    if (pid) edits.protocolId = pid
  }
  if (chosenAlt.value === 'binary') {
    const pid = col.protocols.find(p => p.method === 'binary')?.id
    if (pid) edits.protocolId = pid
  }
  if (lightening.value) edits.overrideNote = overrideNote.value.trim()
  if (mandateAttached.value && suggestion.value?.kind === 'claim-mandate') {
    edits.createsMandate = {
      title: `Mandat — ${title.value.trim()}`,
      domainCircleIds: [...scope.value.circleIds],
      domainTags: [...tags.value],
      durationDays: 90,
      reportEveryDays: 30,
    }
  }
  const applied = decisions.applyPath(created, { ...v, reviewRequired: reviewChecked.value }, edits)
  if ('ok' in applied) { error.value = applied.reason; return }
  if (engraveChecked.value && applied.status === 'adopted') await decisions.engrave(applied.id)
  if (applied.status === 'adopted'
    && (effectiveRoute.value === 'solo' || effectiveRoute.value === 'record' || v.conservatoryChain)) {
    stamping.value = true
    setTimeout(() => navigateTo('/'), 1100)
    return
  }
  await navigateTo(`/decisions/${applied.id}`)
}
</script>

<template>
  <!-- ld-v2 -->
  <div class="decider">
    <div v-if="!col.current" class="ld-card decider__none">
      <p>Aucun collectif actif — ouvre ou crée un collectif d'abord.</p>
      <NuxtLink to="/creer" class="ld-btn">Créer un collectif</NuxtLink>
    </div>

    <template v-else>
      <input
        v-model="title"
        type="text"
        class="decider__title"
        :placeholder="CAPTURE_PLACEHOLDER"
        :aria-label="CAPTURE_PLACEHOLDER"
      >

      <CheminQ0
        :title="title"
        :linked-clause-id="amendsClauseId"
        @contest="amendsClauseId = $event"
      />

      <CheminChips
        v-model:scope="scope"
        v-model:reversibility="reversibility"
        v-model:weight="weight"
        v-model:urgent="urgent"
        v-model:resource-note="resourceNote"
        v-model:resource-amount="resourceAmount"
        v-model:resource-unit="resourceUnit"
        v-model:baseline-note="baselineNote"
        :stack="stack"
        :engages-open="engagesOpen"
        :baseline-suggested="baselineSuggested"
      />

      <template v-if="chemin">
        <CheminPathCard
          :path="chemin"
          :route-shown="effectiveRoute"
          :overridden="overridden"
          :override-label="overrideLabel"
          :stack="stack"
          :self-only="scope.selfOnly"
          :duration-label="durationLabel"
          :threshold-label="thresholdLabel"
          :main-label="mainLabel"
          v-model:review-checked="reviewChecked"
          v-model:engrave-checked="engraveChecked"
          :review-locked="reviewLocked"
          :disabled="disabled"
          :block-reason="blockReason"
          @validate="validate"
        />
        <p v-if="error" class="decider__error">{{ error }}</p>

        <CheminAlternatives
          v-model:chosen="chosenAlt"
          v-model:decided-how="decidedHow"
          v-model:override-note="overrideNote"
          v-model:spec="spec"
          :alternatives="chemin.alternatives"
          :parametric-hint="chemin.parametricHint === true"
          :lightening="lightening"
        />

        <details class="decider__why">
          <summary>{{ WHY_DISCLOSURE }}</summary>
          <p>Règle {{ chemin.rule }} — {{ chemin.explanation }}</p>
          <p>
            Le chemin est recommandé par le Pacte de ton collectif, jamais imposé :
            alourdir est libre en un tap, alléger se motive et s'affiche à tous.
          </p>
        </details>

        <CheminSuggestion
          v-if="suggestion"
          v-model:mandate-attached="mandateAttached"
          :suggestion="suggestion"
        />
      </template>
    </template>

    <!-- Micro-célébration — tampon bref puis retour au Fil -->
    <Transition name="stamp">
      <div v-if="stamping" class="decider__overlay">
        <span class="ld-stamp decider__stamp">井 {{ ADOPTED_STAMP }}</span>
        <p class="decider__toast">{{ ADOPTED_TOAST }}</p>
      </div>
    </Transition>
  </div>
</template>

<style scoped>
.decider {
  width: 100%;
  max-width: 44rem;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.decider__none {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  padding: 2rem;
  text-align: center;
}
.decider__title {
  width: 100%;
  padding: 0.875rem 1rem;
  font-size: clamp(1.125rem, 3.5vw, 1.375rem);
  font-weight: 700;
  letter-spacing: -0.01em;
}
.decider__error { margin: 0; font-size: 0.875rem; font-weight: 600; color: var(--mood-error); }
.decider__why {
  font-size: 0.875rem;
  color: var(--mood-text-muted);
}
.decider__why summary {
  cursor: pointer;
  font-weight: 700;
  padding: 0.25rem;
  border-radius: var(--r-input);
  width: fit-content;
}
.decider__why summary:hover { color: var(--mood-text); }
.decider__why p { margin: 0.5rem 0 0 0.25rem; line-height: 1.5; }
.decider__overlay {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  background: color-mix(in srgb, var(--mood-bg) 88%, transparent);
}
.decider__stamp { font-size: 1.5rem; padding: 0.75rem 1.5rem; }
.decider__toast { margin: 0; font-size: 1rem; font-weight: 600; }
.stamp-enter-active { transition: opacity 0.12s ease; }
.stamp-enter-from { opacity: 0; }
.stamp-enter-active .decider__stamp {
  animation: stamp-in 0.12s ease;
}
@keyframes stamp-in {
  from { transform: rotate(-10deg) scale(1.4); opacity: 0; }
  to { transform: rotate(-10deg) scale(1); opacity: 1; }
}
</style>
