<script setup>
import { computed, onMounted, onBeforeUnmount, ref } from 'vue'
import { storeToRefs } from 'pinia'
import { useSimulationStore } from '@/stores/simulation'
import ScenarioCard from '@/components/simulation/ScenarioCard.vue'
import SessionCard from '@/components/simulation/SessionCard.vue'
import ResetWardPanel from '@/components/simulation/ResetWardPanel.vue'
import SimulationRunPanel from '@/components/simulation/SimulationRunPanel.vue'
import Button from '@/components/ui/Button.vue'
import Skeleton from '@/components/ui/Skeleton.vue'
import EmptyState from '@/components/ui/EmptyState.vue'
import { formatWallClockForSpeed } from '@/composables/simulationFormat'

const SPEED_OPTIONS = [
  { label: 'Real time', multiplier: 1 },
  { label: 'Fast', multiplier: 60 },
  { label: 'Very fast', multiplier: 240 },
  { label: 'Instant', multiplier: 600 },
]

const simulationStore = useSimulationStore()
const {
  scenarios,
  sessions,
  activeRuns,
  recentRuns,
  dataSummary,
  loading,
  error,
  successMessage,
  starting,
  purging,
  atConcurrencyLimit,
  maxConcurrentRuns,
  maxSpeed,
} = storeToRefs(simulationStore)

const activeTab = ref('sessions')
const selectedScenarioId = ref(null)
const searchQuery = ref('')
const activeTag = ref(null)
const speed = ref(60)
const stoppingId = ref(null)
const stoppingAll = ref(false)
const resetPanelRef = ref(null)

const availableSpeeds = computed(() => {
  const cap = maxSpeed.value
  return SPEED_OPTIONS.map((opt) => {
    const multiplier = cap != null ? Math.min(opt.multiplier, cap) : opt.multiplier
    return { ...opt, multiplier }
  }).filter((opt, index, arr) =>
    // Drop Instant (or any option) if a lower option already uses the same capped value
    index === arr.findIndex(o => o.multiplier === opt.multiplier),
  )
})

const selectedScenario = computed(() =>
  scenarios.value.find(s => s.id === selectedScenarioId.value)
    ?? filteredScenarios.value[0]
    ?? null,
)

const allTags = computed(() => {
  const set = new Set()
  for (const s of scenarios.value) {
    for (const tag of s.tags ?? []) set.add(tag)
  }
  return [...set].sort()
})

const filteredScenarios = computed(() => {
  const q = searchQuery.value.trim().toLowerCase()
  return scenarios.value.filter((s) => {
    if (activeTag.value && !(s.tags ?? []).includes(activeTag.value)) return false
    if (!q) return true
    const haystack = [
      s.name,
      s.description,
      s.id,
      s.department,
      ...(s.tags ?? []),
    ].filter(Boolean).join(' ').toLowerCase()
    return haystack.includes(q)
  })
})

const summaryWithActive = computed(() => ({
  ...dataSummary.value,
  activeRuns: activeRuns.value.length,
}))

function wallClockFor(multiplier) {
  if (!selectedScenario.value?.durationMinutes) return null
  return formatWallClockForSpeed(selectedScenario.value.durationMinutes, multiplier)
}

function concurrencyErrorMessage(raw) {
  const n = maxConcurrentRuns.value
  if (/concurrent|concurrency|409/i.test(raw ?? '')) {
    return n > 0
      ? `Concurrency limit reached — at most ${n} simulation run${n === 1 ? '' : 's'} can run at once. Stop an active run first.`
      : 'Concurrency limit reached. Stop an active run first.'
  }
  return raw
}

async function onStart(scenarioId) {
  selectedScenarioId.value = scenarioId
  try {
    await simulationStore.start(scenarioId, speed.value)
  } catch (e) {
    simulationStore.error = concurrencyErrorMessage(e.message)
  }
}

async function onStartSession(sessionId) {
  try {
    await simulationStore.startSessionRun(sessionId, speed.value)
  } catch (e) {
    simulationStore.error = concurrencyErrorMessage(e.message)
  }
}

async function onStop(runId) {
  stoppingId.value = runId
  try {
    await simulationStore.stop(runId)
  } catch {
    // error already on store
  } finally {
    stoppingId.value = null
  }
}

async function onStopAll() {
  stoppingAll.value = true
  try {
    await simulationStore.stopAll()
  } finally {
    stoppingAll.value = false
  }
}

async function onPurge() {
  try {
    await simulationStore.purge()
    resetPanelRef.value?.closeConfirm()
  } catch {
    // error already on store
  }
}

function selectScenario(id) {
  selectedScenarioId.value = id
}

function toggleTag(tag) {
  activeTag.value = activeTag.value === tag ? null : tag
}

function switchTab(tab) {
  activeTab.value = tab
}

onMounted(async () => {
  await Promise.all([
    simulationStore.loadSessions(),
    simulationStore.loadScenarios(),
    simulationStore.refreshRuns(),
    simulationStore.loadDataSummary(),
  ])
  simulationStore.startPolling()
  if (!selectedScenarioId.value && scenarios.value[0]) {
    selectedScenarioId.value = scenarios.value[0].id
  }
  if (!availableSpeeds.value.some(o => o.multiplier === speed.value)) {
    const preferred = availableSpeeds.value.find(o => o.multiplier === 60)
      ?? availableSpeeds.value[1]
      ?? availableSpeeds.value[0]
    if (preferred) speed.value = preferred.multiplier
  }
})

onBeforeUnmount(() => {
  simulationStore.stopPolling()
})
</script>

<template>
  <div class="space-y-8">
    <div>
      <h1 class="text-xl font-bold dark:text-white">Simulation</h1>
      <p class="mt-2 text-sm text-gray-600 dark:text-gray-400">
        Start a testing session or replay an individual scenario. All patients created here are simulated.
      </p>
    </div>

    <div
      v-if="error"
      role="alert"
      class="flex items-start justify-between gap-4 rounded-lg border border-red-200 bg-red-50 px-4 py-3 text-sm text-red-800 dark:border-red-800 dark:bg-red-950/40 dark:text-red-200"
    >
      <p>{{ error }}</p>
      <button
        type="button"
        class="shrink-0 font-medium underline"
        @click="simulationStore.clearError()"
      >
        Dismiss
      </button>
    </div>

    <div
      v-if="successMessage"
      role="status"
      class="flex items-start justify-between gap-4 rounded-lg border border-emerald-200 bg-emerald-50 px-4 py-3 text-sm text-emerald-900 dark:border-emerald-800 dark:bg-emerald-950/40 dark:text-emerald-200"
    >
      <p>{{ successMessage }}</p>
      <button
        type="button"
        class="shrink-0 font-medium underline"
        @click="simulationStore.clearSuccess()"
      >
        Dismiss
      </button>
    </div>

    <ResetWardPanel
      ref="resetPanelRef"
      :summary="summaryWithActive"
      :purging="purging"
      :stopping-all="stoppingAll"
      @purge="onPurge"
      @stop-all="onStopAll"
    />

    <SimulationRunPanel
      v-if="activeRuns.length || recentRuns.length"
      :active-runs="activeRuns"
      :recent-runs="recentRuns"
      :stopping-id="stoppingId"
      @stop="onStop"
    />

    <section aria-labelledby="sim-speed-heading">
      <h2
        id="sim-speed-heading"
        class="mb-3 text-sm font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400"
      >
        Replay speed
      </h2>
      <div
        class="inline-flex max-w-full flex-wrap rounded-lg border border-gray-200 bg-white p-1 dark:border-gray-700 dark:bg-gray-900"
        role="group"
        aria-label="Replay speed"
      >
        <button
          v-for="opt in availableSpeeds"
          :key="opt.label"
          type="button"
          class="min-h-11 rounded-md px-4 py-2 text-left text-sm transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500"
          :class="speed === opt.multiplier
            ? 'bg-blue-600 text-white dark:bg-blue-500'
            : 'text-gray-700 hover:bg-gray-50 dark:text-gray-300 dark:hover:bg-gray-800'"
          :aria-pressed="speed === opt.multiplier"
          @click="speed = opt.multiplier"
        >
          <span class="font-medium">{{ opt.label }}</span>
          <span
            class="mt-0.5 block text-xs"
            :class="speed === opt.multiplier ? 'text-blue-100' : 'text-gray-500 dark:text-gray-400'"
          >
            <template v-if="activeTab === 'scenarios' && wallClockFor(opt.multiplier)">
              {{ wallClockFor(opt.multiplier) }} for selected scenario
            </template>
            <template v-else>
              {{ opt.multiplier }}×
            </template>
          </span>
        </button>
      </div>
      <p
        v-if="activeTab === 'scenarios' && selectedScenario"
        class="mt-2 text-xs text-gray-500 dark:text-gray-400"
      >
        Durations above use
        <span class="font-medium text-gray-700 dark:text-gray-300">{{ selectedScenario.name }}</span>
        ({{ selectedScenario.durationMinutes }} simulated minutes).
      </p>
      <p v-else class="mt-2 text-xs text-gray-500 dark:text-gray-400">
        Session starts use this speed (overriding each preset’s default).
      </p>
    </section>

    <div class="flex gap-2 border-b border-gray-200 dark:border-gray-700">
      <button
        type="button"
        class="border-b-2 px-4 py-2 text-sm font-medium transition"
        :class="activeTab === 'sessions'
          ? 'border-blue-600 text-blue-600 dark:border-blue-400 dark:text-blue-400'
          : 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200'"
        :aria-selected="activeTab === 'sessions'"
        @click="switchTab('sessions')"
      >
        Sessions
      </button>
      <button
        type="button"
        class="border-b-2 px-4 py-2 text-sm font-medium transition"
        :class="activeTab === 'scenarios'
          ? 'border-blue-600 text-blue-600 dark:border-blue-400 dark:text-blue-400'
          : 'border-transparent text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200'"
        :aria-selected="activeTab === 'scenarios'"
        @click="switchTab('scenarios')"
      >
        Scenarios
      </button>
    </div>

    <section v-if="activeTab === 'sessions'" aria-labelledby="sim-sessions-heading">
      <h2
        id="sim-sessions-heading"
        class="mb-4 text-sm font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400"
      >
        Testing sessions
      </h2>

      <Skeleton v-if="loading && sessions.length === 0 && scenarios.length === 0" :rows="3" />

      <EmptyState
        v-else-if="sessions.length === 0"
        message="No session presets found. Check that sessions.json is present beside the scenario directory."
      />

      <div
        v-else
        class="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3"
        role="list"
      >
        <div
          v-for="session in sessions"
          :key="session.id"
          role="listitem"
        >
          <SessionCard
            :session="session"
            :active-run-count="activeRuns.length"
            :max-concurrent-runs="maxConcurrentRuns"
            :starting="starting"
            @start="onStartSession"
          />
        </div>
      </div>
    </section>

    <section v-else aria-labelledby="sim-catalogue-heading">
      <div class="mb-4 flex flex-wrap items-end justify-between gap-4">
        <h2
          id="sim-catalogue-heading"
          class="text-sm font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400"
        >
          Scenario catalogue
        </h2>
      </div>

      <div class="mb-4 space-y-4 rounded-lg border border-gray-200 bg-white p-4 dark:border-gray-700 dark:bg-gray-900">
        <label class="block">
          <span class="sr-only">Search scenarios</span>
          <input
            v-model="searchQuery"
            type="search"
            placeholder="Search scenarios by name, tag, or department…"
            class="w-full rounded-lg border border-gray-200 bg-white px-4 py-2 text-sm text-gray-900 placeholder:text-gray-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-blue-500 dark:border-gray-700 dark:bg-gray-800 dark:text-gray-100"
          >
        </label>

        <div v-if="allTags.length" class="flex flex-wrap items-center gap-2">
          <Button
            v-for="tag in allTags"
            :key="tag"
            size="sm"
            :variant="activeTag === tag ? 'primary' : 'secondary'"
            @click="toggleTag(tag)"
          >
            {{ tag }}
          </Button>
          <Button
            v-if="activeTag"
            size="sm"
            variant="ghost"
            @click="activeTag = null"
          >
            Clear filter
          </Button>
        </div>
      </div>

      <Skeleton v-if="loading && scenarios.length === 0" :rows="3" />

      <EmptyState
        v-else-if="scenarios.length === 0"
        message="No scenarios found. Check that Simulation:ScenarioDirectory is configured and contains scenario JSON files."
      />

      <EmptyState
        v-else-if="filteredScenarios.length === 0"
        message="No scenarios match the current search or tag filter."
      />

      <div
        v-else
        class="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3"
        role="list"
      >
        <div
          v-for="scenario in filteredScenarios"
          :key="scenario.id"
          role="listitem"
        >
          <ScenarioCard
            :scenario="scenario"
            :selected="selectedScenarioId === scenario.id"
            :at-concurrency-limit="atConcurrencyLimit"
            :max-concurrent-runs="maxConcurrentRuns"
            :starting="starting"
            @select="selectScenario"
            @start="onStart"
          />
        </div>
      </div>
    </section>
  </div>
</template>
