"use client"; import { create } from "zustand"; import type { AutotradeCompiledStrategy, AutotradeEngineState, AutotradeRuntimeLog, AutotradeSessionInfo, AutotradeSetupFormValues, AutotradeSignalCandidate, AutotradeValidationResult, } from "@/features/autotrade/types/autotrade.types"; import { resolveSetupDefaults } from "@/lib/autotrade/strategy"; interface AutotradeEngineStoreState { panelOpen: boolean; setupForm: AutotradeSetupFormValues; engineState: AutotradeEngineState; isWorking: boolean; activeSession: AutotradeSessionInfo | null; compiledStrategy: AutotradeCompiledStrategy | null; validation: AutotradeValidationResult | null; lastSignal: AutotradeSignalCandidate | null; orderCountToday: number; cumulativeLossAmount: number; consecutiveFailures: number; lastOrderAtBySymbol: Record; logs: AutotradeRuntimeLog[]; } interface AutotradeEngineStoreActions { setPanelOpen: (open: boolean) => void; patchSetupForm: (patch: Partial) => void; setEngineState: (state: AutotradeEngineState) => void; setWorking: (working: boolean) => void; setActiveSession: (session: AutotradeSessionInfo | null) => void; setCompiledStrategy: (strategy: AutotradeCompiledStrategy | null) => void; setValidation: (validation: AutotradeValidationResult | null) => void; setLastSignal: (signal: AutotradeSignalCandidate | null) => void; increaseOrderCount: (count?: number) => void; addLossAmount: (lossAmount: number) => void; setLastOrderAt: (symbol: string, timestampMs: number) => void; increaseFailure: () => void; resetFailure: () => void; appendLog: ( level: AutotradeRuntimeLog["level"], message: string, options?: { stage?: AutotradeRuntimeLog["stage"]; detail?: string | Record; }, ) => void; clearRuntime: () => void; } const INITIAL_FORM = resolveSetupDefaults(); const INITIAL_STATE: AutotradeEngineStoreState = { panelOpen: false, setupForm: INITIAL_FORM, engineState: "IDLE", isWorking: false, activeSession: null, compiledStrategy: null, validation: null, lastSignal: null, orderCountToday: 0, cumulativeLossAmount: 0, consecutiveFailures: 0, lastOrderAtBySymbol: {}, logs: [], }; export const useAutotradeEngineStore = create< AutotradeEngineStoreState & AutotradeEngineStoreActions >((set) => ({ ...INITIAL_STATE, setPanelOpen: (open) => { set({ panelOpen: open }); }, patchSetupForm: (patch) => { set((state) => ({ setupForm: { ...state.setupForm, ...patch, }, })); }, setEngineState: (engineState) => { set({ engineState }); }, setWorking: (isWorking) => { set({ isWorking }); }, setActiveSession: (activeSession) => { set({ activeSession }); }, setCompiledStrategy: (compiledStrategy) => { set({ compiledStrategy }); }, setValidation: (validation) => { set({ validation }); }, setLastSignal: (lastSignal) => { set({ lastSignal }); }, increaseOrderCount: (count = 1) => { set((state) => ({ orderCountToday: state.orderCountToday + Math.max(1, count), })); }, addLossAmount: (lossAmount) => { set((state) => ({ cumulativeLossAmount: state.cumulativeLossAmount + Math.max(0, Math.floor(lossAmount)), })); }, setLastOrderAt: (symbol, timestampMs) => { set((state) => ({ lastOrderAtBySymbol: { ...state.lastOrderAtBySymbol, [symbol]: timestampMs, }, })); }, increaseFailure: () => { set((state) => ({ consecutiveFailures: state.consecutiveFailures + 1, })); }, resetFailure: () => { set({ consecutiveFailures: 0 }); }, appendLog: (level, message, options) => { const entry: AutotradeRuntimeLog = { id: safeLogId(), level, stage: options?.stage, message, detail: normalizeLogDetail(options?.detail), createdAt: new Date().toISOString(), }; set((state) => ({ logs: [entry, ...state.logs].slice(0, 80), })); }, clearRuntime: () => { set((state) => ({ ...state, engineState: "IDLE", isWorking: false, activeSession: null, compiledStrategy: null, validation: null, lastSignal: null, orderCountToday: 0, cumulativeLossAmount: 0, consecutiveFailures: 0, lastOrderAtBySymbol: {}, })); }, })); function safeLogId() { if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") { return crypto.randomUUID(); } return `autotrade-log-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`; } function normalizeLogDetail(detail: string | Record | undefined) { if (!detail) { return undefined; } if (typeof detail === "string") { const cleaned = detail.trim(); return cleaned.length > 0 ? cleaned : undefined; } try { return JSON.stringify(detail, null, 2); } catch { return undefined; } }