<script setup lang="ts">
import { computed } from "vue"

const props = defineProps<{ activeId: string }>()

const WEEKDAYS = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]

// A month event bar, placed by (week row, start weekday, day span).
interface MonthEvent {
  key: string
  label: string
  week: number
  start: number // 0 = Mon … 6 = Sun
  span: number // days
  clip?: boolean // clipped at the viewport boundary
  skeleton?: boolean
  hl?: boolean
  hlTag?: string
}
// A week-view event block, placed on the time axis.
interface WeekEvent {
  key: string
  label: string
  day: number // 0 = Mon … 4 = Fri
  hour: number // 0-based slot from the first row
  hl?: boolean
}
// Shared header/chrome fields — both scopes have a navigable title + scope toggle.
interface BaseFrame {
  title: string
  scopeActive: "Day" | "Week" | "Month"
  navHl?: boolean
  scopeHl?: boolean
  note?: string
}
interface MonthFrame extends BaseFrame {
  scope: "month"
  gridHl?: boolean
  gridTag?: string
  badge?: string
  badgeNote?: string
  events: MonthEvent[]
}
interface WeekFrame extends BaseFrame {
  scope: "week"
  hours: string[]
  events: WeekEvent[]
}
type Frame = MonthFrame | WeekFrame

// April 2026, laid out as in the ontology README (Mon-first weeks).
// Leading 30 (Mar) and trailing 1-4 (May) are adjacent-month days.
const WEEKS: { day: number; adjacent?: boolean }[][] = [
  [30, 1, 2, 3, 4, 5, 6].map((d, i) => ({ day: d, adjacent: i === 0 })),
  [7, 8, 9, 10, 11, 12, 13].map((d) => ({ day: d })),
  [14, 15, 16, 17, 18, 19, 20].map((d) => ({ day: d })),
  [21, 22, 23, 24, 25, 26, 27].map((d) => ({ day: d })),
  [28, 29, 30, 1, 2, 3, 4].map((d, i) => ({ day: d, adjacent: i >= 3 })),
]

// Events used across the month frames.
const meeting = (extra: Partial<MonthEvent> = {}): MonthEvent => ({
  key: "meeting",
  label: "Meeting",
  week: 0,
  start: 2,
  span: 2,
  ...extra,
})
const sprint = (extra: Partial<MonthEvent> = {}): MonthEvent => ({
  key: "sprint",
  label: "Sprint review",
  week: 1,
  start: 0,
  span: 1,
  ...extra,
})
const review = (extra: Partial<MonthEvent> = {}): MonthEvent => ({
  key: "review",
  label: "Design review",
  week: 3,
  start: 1,
  span: 3,
  ...extra,
})

const frame = computed<Frame>(() => {
  switch (props.activeId) {
    case "full":
      return {
        scope: "month",
        title: "April 2026",
        scopeActive: "Month",
        navHl: true,
        scopeHl: true,
        gridHl: true,
        gridTag: "◈ RENDER",
        events: [meeting(), sprint(), review()],
      }
    case "navigate":
      return {
        scope: "month",
        title: "April 2026",
        scopeActive: "Month",
        navHl: true,
        badge: "loadState = Loading",
        badgeNote: "navigate(+1) → fetching May 2026…",
        note: "navigate(+1) advances focusDate by one scope unit (a month here) → loadState ← Loading, viewport re-fetched.",
        events: [meeting(), sprint(), review()],
      }
    case "render":
      return {
        scope: "month",
        title: "April 2026",
        scopeActive: "Month",
        gridHl: true,
        gridTag: "◈ RENDER",
        note: "visible = { e ∈ members | e.startAt ∈ [viewportStart, viewportEnd) } — the trailing event is clipped at viewportEnd; its data is not truncated.",
        events: [
          meeting({ hl: true, hlTag: "spans Wed–Thu" }),
          sprint(),
          review(),
          { key: "overflow", label: "Offsite ▸", week: 4, start: 4, span: 3, clip: true },
        ],
      }
    case "load":
      return {
        scope: "month",
        title: "April 2026",
        scopeActive: "Month",
        badge: "loadState = Refreshing",
        badgeNote: "re-fetching viewport range…",
        note: "Skeleton bars mirror real event bars so the transition to Idle causes no layout shift. members = ∅ is a valid result, not an error.",
        events: [
          meeting({ skeleton: true }),
          sprint({ skeleton: true }),
          review({ skeleton: true }),
        ],
      }
    case "scope":
      return {
        scope: "week",
        title: "Week of Apr 21",
        scopeActive: "Week",
        scopeHl: true,
        note: "changeScope(Week) keeps focusDate (Apr 21) and re-fetches the new [viewportStart, viewportEnd) — layout switches to a time axis.",
        hours: ["09:00", "10:00", "11:00", "12:00"],
        events: [
          { key: "standup", label: "Stand-up", day: 0, hour: 0 },
          { key: "oneone", label: "1:1", day: 4, hour: 0 },
          { key: "design", label: "Design review", day: 2, hour: 2, hl: true },
        ],
      }
    default:
      return {
        scope: "month",
        title: "April 2026",
        scopeActive: "Month",
        events: [meeting(), sprint(), review()],
      }
  }
})

const isMonth = computed(() => frame.value.scope === "month")

function barStyle(e: MonthEvent) {
  return {
    left: `calc(${(e.start / 7) * 100}% + 3px)`,
    width: `calc(${(e.span / 7) * 100}% - 6px)`,
  }
}
</script>

<template>
  <div class="specimen">
    <!-- header: scope toggle + navigation -->
    <div class="cal-head">
      <div
        class="nav"
        :class="frame.navHl ? ['hl', 'tag-b'] : []"
        :data-tag="frame.navHl ? '◈ NAVIGATE' : undefined"
      >
        <span class="arrow">‹</span>
        <span class="title">{{ frame.title }}</span>
        <span class="arrow">›</span>
      </div>
      <div
        class="scopes"
        :class="frame.scopeHl ? ['hl', 'tag-b'] : []"
        :data-tag="frame.scopeHl ? '◈ SCOPE' : undefined"
      >
        <span
          v-for="s in ['Day', 'Week', 'Month']"
          :key="s"
          class="scope-btn"
          :class="{ on: frame.scopeActive === s }"
          >{{ s }}</span
        >
      </div>
    </div>

    <div v-if="frame.scope === 'month' && frame.badge" class="spec-toolbar">
      <span class="badge">{{ frame.badge }}</span>
      <span v-if="frame.badgeNote" class="badge-note">{{ frame.badgeNote }}</span>
    </div>

    <p v-if="frame.note" class="note">{{ frame.note }}</p>

    <!-- MONTH grid -->
    <div
      v-if="isMonth && frame.scope === 'month'"
      class="monthbox"
      :class="frame.gridHl ? ['hl', 'tag-b'] : []"
      :data-tag="frame.gridTag"
    >
      <div class="wd-row">
        <div v-for="d in WEEKDAYS" :key="d" class="wd">{{ d }}</div>
      </div>
      <div v-for="(week, wi) in WEEKS" :key="wi" class="week">
        <div class="daycells">
          <div v-for="(c, ci) in week" :key="ci" class="daycell" :class="{ adjacent: c.adjacent }">
            <span class="dnum">{{ c.day }}</span>
          </div>
        </div>
        <div
          v-for="e in frame.events.filter((x) => x.week === wi)"
          :key="e.key"
          class="ev"
          :class="{ skeleton: e.skeleton, clip: e.clip, hl: e.hl }"
          :style="barStyle(e)"
          :data-tag="e.hlTag"
        >
          <span v-if="!e.skeleton" class="ev-label">{{ e.label }}</span>
        </div>
      </div>
    </div>

    <!-- WEEK time-axis grid -->
    <div v-else-if="frame.scope === 'week'" class="weekbox">
      <div class="wk-head">
        <div class="wk-corner" />
        <div v-for="d in WEEKDAYS.slice(0, 5)" :key="d" class="wk-day">{{ d }}</div>
      </div>
      <div v-for="(h, hi) in frame.hours" :key="h" class="wk-row">
        <div class="wk-hour">{{ h }}</div>
        <div v-for="di in 5" :key="di" class="wk-cell">
          <div
            v-for="e in frame.events.filter((x) => x.day === di - 1 && x.hour === hi)"
            :key="e.key"
            class="wk-ev"
            :class="{ hl: e.hl }"
          >
            {{ e.label }}
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<style scoped>
.specimen {
  --ink: var(--color-base-content);
  --ink-faint: #4f7099; /* decorative only: borders, stripes, fills */
  --ink-dim: #7ea6cd; /* dim text — meets WCAG AA on navy paper */
  --amber: var(--color-accent);
  --amber-dim: color-mix(in srgb, var(--color-accent) 14%, transparent);
  --cyan: var(--color-secondary);
  --red: var(--color-error);
  --paper: var(--color-base-100);
  --line: rgba(200, 226, 255, 0.14);
  --line-strong: rgba(200, 226, 255, 0.3);
  font-size: 13px;
}
.note {
  color: var(--ink-dim);
  font-size: 11px;
  font-style: italic;
  margin: 0 0 10px;
}

/* header */
.cal-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-bottom: 12px;
  flex-wrap: wrap;
}
.nav {
  display: flex;
  align-items: center;
  gap: 12px;
  border: 1px solid var(--line);
  background: var(--paper);
  padding: 5px 12px;
}
.nav .arrow {
  color: var(--cyan);
  font-size: 16px;
  line-height: 1;
}
.nav .title {
  color: var(--ink);
  letter-spacing: 0.04em;
}
.scopes {
  display: flex;
  border: 1px solid var(--line);
}
.scope-btn {
  padding: 4px 11px;
  font-size: 11px;
  color: var(--ink-dim);
  border-left: 1px solid var(--line);
}
.scope-btn:first-child {
  border-left: none;
}
.scope-btn.on {
  color: var(--amber);
  background: var(--amber-dim);
}

/* toolbar */
.spec-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 10px;
  font-size: 12px;
}
.badge {
  border: 1px solid var(--line-strong);
  color: var(--cyan);
  padding: 1px 7px;
  font-size: 10px;
  letter-spacing: 0.08em;
}
.badge-note {
  color: var(--ink-dim);
}

/* month grid */
.monthbox {
  border: 1px solid var(--line);
  background: var(--paper);
}
.wd-row {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  border-bottom: 1px solid var(--line);
}
.wd {
  padding: 5px 6px;
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--ink) 55%, transparent);
  text-align: right;
}
.week {
  position: relative;
  border-bottom: 1px solid var(--line);
}
.week:last-child {
  border-bottom: none;
}
.daycells {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
}
.daycell {
  min-height: 46px;
  border-left: 1px dashed var(--line);
  padding: 4px 6px;
}
.daycell:first-child {
  border-left: none;
}
.dnum {
  font-size: 11px;
  color: color-mix(in srgb, var(--ink) 70%, transparent);
}
.daycell.adjacent .dnum {
  color: var(--ink-dim);
  opacity: 0.55;
}
/* event bars, positioned over the day cells of their week */
.ev {
  position: absolute;
  top: 22px;
  height: 18px;
  display: flex;
  align-items: center;
  padding: 0 6px;
  border: 1px solid var(--cyan);
  background: color-mix(in srgb, var(--cyan) 16%, transparent);
  font-size: 10px;
  color: var(--ink);
  white-space: nowrap;
  overflow: hidden;
}
.ev-label {
  overflow: hidden;
  text-overflow: ellipsis;
}
.ev.clip {
  border-right-style: dashed;
  border-color: var(--amber);
  background: var(--amber-dim);
  color: var(--amber);
  -webkit-mask-image: linear-gradient(90deg, #000 78%, transparent);
  mask-image: linear-gradient(90deg, #000 78%, transparent);
}
.ev.skeleton {
  border-style: solid;
  border-color: var(--line-strong);
  background: rgba(255, 255, 255, 0.02);
}
.ev.skeleton::after {
  content: "";
  width: 62%;
  height: 8px;
  background: repeating-linear-gradient(
    90deg,
    var(--ink-faint),
    var(--ink-faint) 6px,
    transparent 6px,
    transparent 12px
  );
  opacity: 0.5;
}

/* week / time-axis grid */
.weekbox {
  border: 1px solid var(--line);
  background: var(--paper);
}
.wk-head,
.wk-row {
  display: grid;
  grid-template-columns: 48px repeat(5, 1fr);
}
.wk-head {
  border-bottom: 1px solid var(--line);
}
.wk-corner {
  border-right: 1px solid var(--line);
}
.wk-day {
  padding: 5px 6px;
  font-size: 10px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--ink) 55%, transparent);
  text-align: center;
}
.wk-row {
  border-bottom: 1px solid var(--line);
}
.wk-row:last-child {
  border-bottom: none;
}
.wk-hour {
  padding: 6px;
  font-size: 10px;
  color: var(--ink-dim);
  border-right: 1px solid var(--line);
  text-align: right;
}
.wk-cell {
  min-height: 30px;
  border-left: 1px dashed var(--line);
  padding: 3px;
}
.wk-cell:first-of-type {
  border-left: none;
}
.wk-ev {
  border: 1px solid var(--cyan);
  background: color-mix(in srgb, var(--cyan) 16%, transparent);
  color: var(--ink);
  font-size: 10px;
  padding: 2px 5px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* callouts */
.hl {
  outline: 1.5px dashed var(--amber);
  outline-offset: 3px;
  position: relative;
  z-index: 2;
}
.hl[data-tag]::after {
  content: attr(data-tag);
  position: absolute;
  top: -10px;
  left: 8px;
  background: var(--paper);
  color: var(--amber);
  font-size: 9px;
  padding: 0 5px;
  border: 1px solid var(--amber);
  white-space: nowrap;
  letter-spacing: 0.1em;
  line-height: 1.5;
  z-index: 3;
}
.hl.tag-b[data-tag]::after {
  top: auto;
  bottom: -10px;
}
.ev.hl {
  z-index: 4;
}
.wk-ev.hl {
  outline-color: var(--amber);
  border-color: var(--amber);
  background: var(--amber-dim);
  color: var(--amber);
}
</style>
