import { defineStore } from 'pinia'
import { ref, computed, watch } from 'vue'
import {
  fetchSimulationConfig,
  fetchScenarios,
  fetchSessions,
  fetchRuns,
  startRun,
  startSession,
  stopRun,
  fetchDataSummary,
  purgeData,
} from '@/api/simulation'
import { useWardStore } from '@/stores/ward'
import { useAlertStore } from '@/stores/alerts'

const ACTIVE_STATUSES = new Set(['PENDING', 'RUNNING'])
const POLL_INTERVAL_MS = 2_000

function isActiveStatus(status) {
  return ACTIVE_STATUSES.has(status)
}

export const useSimulationStore = defineStore('simulation', () => {
  const enabled = ref(false)
  const maxSpeed = ref(null)
  const maxConcurrentRuns = ref(0)
  const scenarios = ref([])
  const sessions = ref([])
  const runs = ref([])
  const dataSummary = ref({
    simulatedPatients: 0,
    encounters: 0,
    observations: 0,
    alerts: 0,
    activeRuns: 0,
  })
  const loading = ref(false)
  const error = ref(null)
  const successMessage = ref(null)
  const starting = ref(false)
  const purging = ref(false)

  let pollTimer = null
  let stopActiveWatch = null

  const activeRuns = computed(() =>
    runs.value.filter(r => isActiveStatus(r.status)),
  )

  const recentRuns = computed(() =>
    runs.value
      .filter(r => !isActiveStatus(r.status))
      .slice()
      .sort((a, b) => new Date(b.startedAt) - new Date(a.startedAt))
      .slice(0, 10),
  )

  const hasActiveRun = computed(() => activeRuns.value.length > 0)

  const atConcurrencyLimit = computed(() =>
    activeRuns.value.length >= maxConcurrentRuns.value,
  )

  const scenariosByTag = computed(() => {
    const groups = {}
    for (const scenario of scenarios.value) {
      for (const tag of scenario.tags ?? []) {
        if (!groups[tag]) groups[tag] = []
        groups[tag].push(scenario)
      }
    }
    return groups
  })

  function clearError() {
    error.value = null
  }

  function clearSuccess() {
    successMessage.value = null
  }

  function sessionFitsCapacity(session) {
    const need = session?.scenarios?.length ?? 0
    if (!need || !maxConcurrentRuns.value) return false
    return activeRuns.value.length + need <= maxConcurrentRuns.value
  }

  async function loadConfig() {
    try {
      const data = await fetchSimulationConfig()
      enabled.value = !!data?.enabled
      maxSpeed.value = data?.maxSpeed ?? null
      maxConcurrentRuns.value = data?.maxConcurrentRuns ?? 0
    } catch {
      // Fail closed — never surface simulation controls on a broken probe.
      enabled.value = false
      maxSpeed.value = null
      maxConcurrentRuns.value = 0
    }
  }

  async function loadScenarios() {
    loading.value = true
    error.value = null
    try {
      const data = await fetchScenarios()
      scenarios.value = Array.isArray(data) ? data : []
    } catch (e) {
      error.value = e.message
      scenarios.value = []
    } finally {
      loading.value = false
    }
  }

  async function loadSessions() {
    error.value = null
    try {
      const data = await fetchSessions()
      sessions.value = Array.isArray(data) ? data : []
    } catch (e) {
      error.value = e.message
      sessions.value = []
    }
  }

  async function loadDataSummary() {
    try {
      const data = await fetchDataSummary()
      dataSummary.value = {
        simulatedPatients: data?.simulatedPatients ?? 0,
        encounters: data?.encounters ?? 0,
        observations: data?.observations ?? 0,
        alerts: data?.alerts ?? 0,
        activeRuns: data?.activeRuns ?? activeRuns.value.length,
      }
    } catch (e) {
      error.value = e.message
    }
  }

  function detectNewlyRunning(previousById, nextRuns) {
    return nextRuns.some((run) => {
      if (run.status !== 'RUNNING') return false
      const prev = previousById.get(run.runId)
      return prev !== 'RUNNING'
    })
  }

  async function refreshRuns() {
    try {
      const previousById = new Map(runs.value.map(r => [r.runId, r.status]))
      const data = await fetchRuns()
      const next = Array.isArray(data) ? data : []

      // Keep optimistic placeholders until the server echoes a real run.
      const optimistic = runs.value.filter(r => r._optimistic)
      const serverIds = new Set(next.map(r => r.runId))
      const stillPending = optimistic.filter(r => !serverIds.has(r.runId))
      runs.value = [...stillPending, ...next]

      if (detectNewlyRunning(previousById, next)) {
        useWardStore().loadEncounters()
        loadDataSummary()
      }
    } catch (e) {
      error.value = e.message
    }
  }

  async function start(scenarioId, speed) {
    starting.value = true
    error.value = null
    successMessage.value = null

    const scenario = scenarios.value.find(s => s.id === scenarioId)
    const placeholderId = `optimistic-${Date.now()}`
    const placeholder = {
      runId: placeholderId,
      scenarioId,
      scenarioName: scenario?.name ?? scenarioId,
      status: 'PENDING',
      speed,
      patientId: null,
      encounterId: null,
      sessionId: null,
      patientDisplayName: '',
      startedAt: new Date().toISOString(),
      elapsedRealSeconds: 0,
      lastOffsetMinutes: 0,
      totalOffsetMinutes: scenario?.durationMinutes ?? 0,
      progressPercent: 0,
      observationsSent: 0,
      medicationsSent: 0,
      ordersPlaced: 0,
      failureReason: null,
      _optimistic: true,
    }
    runs.value = [placeholder, ...runs.value]

    try {
      const run = await startRun({ scenarioId, speed })
      runs.value = [
        run,
        ...runs.value.filter(r => r.runId !== placeholderId && r.runId !== run.runId),
      ]
      if (run.status === 'RUNNING') {
        useWardStore().loadEncounters()
      }
      await loadDataSummary()
      return run
    } catch (e) {
      runs.value = runs.value.filter(r => r.runId !== placeholderId)
      error.value = e.message
      throw e
    } finally {
      starting.value = false
    }
  }

  async function startSessionRun(sessionId, speed) {
    starting.value = true
    error.value = null
    successMessage.value = null
    try {
      const session = await startSession(sessionId, { speed })
      await refreshRuns()
      useWardStore().loadEncounters()
      await loadDataSummary()
      return session
    } catch (e) {
      error.value = e.message
      throw e
    } finally {
      starting.value = false
    }
  }

  async function stop(runId) {
    error.value = null
    try {
      const run = await stopRun(runId)
      const idx = runs.value.findIndex(r => r.runId === runId)
      if (idx >= 0) {
        runs.value[idx] = run
      } else {
        runs.value = [run, ...runs.value]
      }
      await loadDataSummary()
      return run
    } catch (e) {
      error.value = e.message
      throw e
    }
  }

  async function stopAll() {
    error.value = null
    const ids = activeRuns.value.map(r => r.runId)
    for (const id of ids) {
      try {
        await stop(id)
      } catch {
        // continue stopping others; last error stays on store
      }
    }
    await refreshRuns()
    await loadDataSummary()
  }

  async function purge() {
    purging.value = true
    error.value = null
    successMessage.value = null
    try {
      const result = await purgeData()
      runs.value = []
      await Promise.all([
        loadDataSummary(),
        useWardStore().loadEncounters(),
        useAlertStore().loadGlobalAlerts(),
      ])
      const patients = result?.patientsDeleted ?? 0
      const alerts = result?.alertsDeleted ?? 0
      successMessage.value =
        `Ward reset — removed ${patients} simulated patient${patients === 1 ? '' : 's'}` +
        ` and ${alerts} alert${alerts === 1 ? '' : 's'}.`
      return result
    } catch (e) {
      error.value = e.message
      throw e
    } finally {
      purging.value = false
    }
  }

  function syncPollTimer(active) {
    if (active) {
      if (!pollTimer) {
        pollTimer = setInterval(() => {
          refreshRuns()
        }, POLL_INTERVAL_MS)
      }
    } else if (pollTimer) {
      clearInterval(pollTimer)
      pollTimer = null
    }
  }

  /** View-owned lifecycle: start watching active runs and poll while any are in flight. */
  function startPolling() {
    if (stopActiveWatch) return
    stopActiveWatch = watch(hasActiveRun, (active) => {
      syncPollTimer(active)
    }, { immediate: true })
  }

  function stopPolling() {
    if (stopActiveWatch) {
      stopActiveWatch()
      stopActiveWatch = null
    }
    syncPollTimer(false)
  }

  return {
    enabled,
    maxSpeed,
    maxConcurrentRuns,
    scenarios,
    sessions,
    runs,
    dataSummary,
    loading,
    error,
    successMessage,
    starting,
    purging,
    activeRuns,
    recentRuns,
    hasActiveRun,
    atConcurrencyLimit,
    scenariosByTag,
    clearError,
    clearSuccess,
    sessionFitsCapacity,
    loadConfig,
    loadScenarios,
    loadSessions,
    loadDataSummary,
    refreshRuns,
    start,
    startSessionRun,
    stop,
    stopAll,
    purge,
    startPolling,
    stopPolling,
  }
})
