<script setup lang="ts">
// <!-- ld-v2 --> Vertical lifecycle timeline: full dots linked by a background
// line, traversed states with timestamps, ONE state→color mapping. The paused
// dot is striped when the boundary is contested; an extended non-easy window
// shows deep amber + « il manque un accord explicite ».
import type { Decision, DecisionStatus, VoteSession } from '~/types/domain'
import {
  ASSENT_MISSING, BOUNDARY_SUSPENDED, FROZEN_LABEL, STATUS_LABELS,
} from '~/lexicon'
import { useCollectiveStore } from '~/stores/collective'
import { STATUS_TOKEN, dateTimeFr, type DisplayStatus } from './decisionUi'

const props = defineProps<{ decision: Decision; session?: VoteSession }>()

const col = useCollectiveStore()

interface Step {
  key: DisplayStatus
  label: string
  at?: string
  state: 'done' | 'current' | 'ahead'
  token: string
  suspended?: boolean
  extended?: boolean
}

const hasThirdPartyAssent = computed(() =>
  col.assents.some(a =>
    a.decisionId === props.decision.id && a.personId !== props.decision.authorId))

/** Extended window: non-easy deadline passed without an explicit agreement. */
const isExtended = computed(() => {
  const d = props.decision
  return d.status === 'objection'
    && d.reversibility !== 'easy'
    && !d.windowSuspendedAt
    && d.windowEndsAt !== undefined
    && new Date(d.windowEndsAt).getTime() < Date.now()
    && !hasThirdPartyAssent.value
})

/** The path of this decision — traversed states first, expected end last. */
const steps = computed<Step[]>(() => {
  const d = props.decision
  const s = props.session
  const path: { key: DisplayStatus; at?: string }[] = [{ key: 'draft', at: d.createdAt }]

  if (d.route === 'mandate') path.push({ key: 'objection', at: d.createdAt })
  else if (d.route === 'advice') path.push({ key: 'advice', at: d.createdAt })
  else if (d.route === 'collective') {
    const framed = d.status === 'framing'
      || (d.status === 'closed' && !d.decidedAt)
      || col.decisions.some(c => c.parentDecisionId === d.id && c.chainKind === 'element')
    if (framed) path.push({ key: 'framing', at: d.createdAt })
    if (d.status !== 'framing' || s) path.push({ key: 'voting', at: s?.opensAt })
    if (s?.status === 'frozen') path.push({ key: 'frozen', at: s.closesAt })
  }

  const terminals: DecisionStatus[] = ['adopted', 'rejected', 'revoked', 'closed', 'transmitted']
  if (d.status === 'revoked') {
    path.push({ key: 'adopted', at: d.decidedAt }, { key: 'revoked', at: d.updatedAt })
  } else if (d.status === 'closed' && d.decidedAt) {
    path.push({ key: 'adopted', at: d.decidedAt }, { key: 'closed', at: d.updatedAt })
  } else if (terminals.includes(d.status)) {
    path.push({ key: d.status, at: d.decidedAt ?? d.updatedAt })
  } else {
    // Expected landing of the route, shown ahead.
    path.push({ key: d.route === 'transmit' ? 'transmitted' : 'adopted' })
  }

  const shownKey: DisplayStatus = d.status === 'voting' && s?.status === 'frozen'
    ? 'frozen'
    : d.status
  const currentIndex = path.findIndex(p => p.key === shownKey)

  return path.map((p, i) => ({
    key: p.key,
    label: p.key === 'frozen' ? FROZEN_LABEL : STATUS_LABELS[p.key as DecisionStatus],
    ...(p.at !== undefined ? { at: p.at } : {}),
    state: currentIndex === -1 || i < currentIndex
      ? 'done'
      : i === currentIndex ? 'current' : 'ahead',
    token: STATUS_TOKEN[p.key],
    suspended: i === currentIndex && !!d.windowSuspendedAt
      && (p.key === 'objection' || p.key === 'advice'),
    extended: i === currentIndex && p.key === 'objection' && isExtended.value,
  }))
})
</script>

<template>
  <!-- ld-v2 -->
  <ol class="tl" aria-label="Cycle de vie">
    <li
      v-for="step in steps"
      :key="step.key"
      class="tl__step"
      :class="[`tl__step--${step.state}`, { 'tl__step--extended': step.extended }]"
      :style="{
        '--step-color': `var(--mood-status-${step.token})`,
        '--step-bg': `var(--mood-status-${step.token}-bg)`,
      }"
    >
      <span class="tl__dot" :class="{ 'tl__dot--paused': step.suspended }">
        <UIcon v-if="step.suspended" name="i-lucide-pause" class="tl__pause" />
      </span>
      <span class="tl__body">
        <span class="tl__label">{{ step.label }}</span>
        <span v-if="step.at && step.state !== 'ahead'" class="tl__date">
          {{ dateTimeFr(step.at) }}
        </span>
        <span v-if="step.suspended" class="tl__note tl__note--paused">
          {{ BOUNDARY_SUSPENDED }}
        </span>
        <span v-else-if="step.extended" class="tl__note tl__note--extended">
          {{ ASSENT_MISSING }} — la fenêtre se prolonge
        </span>
      </span>
    </li>
  </ol>
</template>

<style scoped>
.tl {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.tl__step {
  position: relative;
  display: flex;
  gap: 0.75rem;
  padding: 0 0 1.125rem 0;
}
.tl__step:last-child { padding-bottom: 0; }
/* Trait de fond reliant les pastilles */
.tl__step:not(:last-child)::before {
  content: '';
  position: absolute;
  left: 8px;
  top: 18px;
  bottom: 0;
  width: 2px;
  background: var(--mood-accent-soft);
}
.tl__dot {
  position: relative;
  z-index: 1;
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  border-radius: 50%;
  background: var(--step-color);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-top: 1px;
}
.tl__step--ahead .tl__dot {
  background: var(--step-bg);
  box-shadow: inset 0 0 0 2px var(--step-color);
  opacity: 0.55;
}
.tl__dot--paused {
  background: repeating-linear-gradient(
    -45deg,
    var(--mood-status-fenetre),
    var(--mood-status-fenetre) 3px,
    var(--mood-status-fenetre-bg) 3px,
    var(--mood-status-fenetre-bg) 6px
  );
}
.tl__pause {
  font-size: 0.625rem;
  color: var(--mood-surface);
}
.tl__body {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  min-width: 0;
}
.tl__label {
  font-size: 0.9375rem;
  font-weight: 700;
  color: var(--step-color);
  line-height: 1.25;
}
.tl__step--ahead .tl__label { opacity: 0.5; }
.tl__step--current .tl__label { font-size: 1rem; }
.tl__date {
  font-size: 0.8125rem;
  color: var(--mood-text-muted);
}
.tl__note {
  margin-top: 0.25rem;
  font-size: 0.8125rem;
  font-weight: 600;
  padding: 3px 10px;
  border-radius: var(--r-pill);
  width: fit-content;
}
.tl__note--paused {
  color: var(--mood-status-fenetre);
  background: repeating-linear-gradient(
    -45deg,
    var(--mood-status-fenetre-bg),
    var(--mood-status-fenetre-bg) 6px,
    transparent 6px,
    transparent 10px
  );
}
.tl__note--extended {
  color: var(--mood-surface);
  background: var(--mood-status-fenetre);
}
.tl__step--extended .tl__dot { box-shadow: 0 0 0 3px var(--mood-status-fenetre-bg); }
</style>
