import { create } from 'zustand'
import { v4 as uuidv4 } from 'uuid'
import type { SimulationResult } from '@/domain/types'
import { simulationRepo } from '@/persistence/repositories'

// Excepción arquitectónica documentada: simulation-store importa process-store en una dirección
// para suscribirse a cambios de datos y invalidar resultados. No existe dependencia inversa.
import { useProcessStore } from '@/store/process-store'

interface SimulationState {
  resultActual: SimulationResult | null      // resultado del escenario actual
  resultAutomated: SimulationResult | null   // resultado del escenario automatizado
  lastSimulatedAt: Date | null               // timestamp de la última simulación exitosa
  lastPersistedExecutedAt: number | null     // executedAt de la última sim guardada en IndexedDB (sobrevive recargas)
  isRunning: boolean
  error: string | null
  selectedActivityId: string | null

  // Persiste ambos resultados en IndexedDB y actualiza el estado en memoria.
  // REGLA: los dos resultados siempre se guardan y actualizan en conjunto.
  persistResults: (processId: string, actual: SimulationResult, automated: SimulationResult) => Promise<void>
  setRunning: (running: boolean) => void
  setError: (error: string | null) => void
  selectActivity: (bpmnElementId: string | null) => void
  // Invalida AMBOS resultados a null. Se llama cuando cambian datos del proceso.
  invalidateResults: () => void
  reset: () => void
  loadLatestForProcess: (processId: string) => Promise<void>
}

export const useSimulationStore = create<SimulationState>((set) => ({
  resultActual: null,
  resultAutomated: null,
  lastSimulatedAt: null,
  lastPersistedExecutedAt: null,
  isRunning: false,
  error: null,
  selectedActivityId: null,

  persistResults: async (processId, actual, automated) => {
    const executedAt = Date.now()
    await simulationRepo.save({
      id: uuidv4(),
      processId,
      executedAt,
      result: actual,
      resultAutomated: automated,
    })
    set({
      resultActual: actual,
      resultAutomated: automated,
      lastSimulatedAt: new Date(),
      lastPersistedExecutedAt: executedAt,
      error: null,
    })
  },

  setRunning: (isRunning) => set({ isRunning }),
  setError: (error) => set({ error }),
  selectActivity: (selectedActivityId) => set({ selectedActivityId }),

  invalidateResults: () =>
    set({ resultActual: null, resultAutomated: null, lastSimulatedAt: null }),

  reset: () =>
    set({
      resultActual: null,
      resultAutomated: null,
      lastSimulatedAt: null,
      lastPersistedExecutedAt: null,
      isRunning: false,
      error: null,
      selectedActivityId: null,
    }),

  loadLatestForProcess: async (processId) => {
    const sim = await simulationRepo.getLatestByProcess(processId)
    set({ lastPersistedExecutedAt: sim?.executedAt ?? null })
  },
}))

// Suscripción reactiva: cualquier cambio en activities o currentProcess del process-store
// invalida los resultados para garantizar que nunca haya resultados "viejos" visibles.
// La comparación por referencia de Zustand asegura que solo fires en cambios reales.
useProcessStore.subscribe((state, prevState) => {
  if (
    state.activities !== prevState.activities ||
    state.currentProcess !== prevState.currentProcess
  ) {
    useSimulationStore.getState().invalidateResults()
  }
})
