import { getLastPrice, getPrice } from '@/api/home' import type { Price, PriceUp } from '@/models/home' import { ref, watch } from 'vue' import { priceSocketConnected, priceStreamLifecycle, priceWssEnabled, shouldUsePricePolling, } from '@/utils/priceSocketState' const LAST_INTERVAL_MS = 10000 let lastInterval: ReturnType | undefined let lastSubscribers = 0 /** @deprecated از lastPrice استفاده کنید — برای سازگاری با کد قدیمی sync می‌شود */ export const price = ref() export const lastPrice = ref() export const olderPrice = ref(0) let lastPriceSourceAt = 0 let consecutivePriceFetchFailures = 0 function hasValidPricePayload(data?: Price | null): boolean { if (!data) { return false } return ( getTradeBuyPrice(data) > 0 || getTradeSellPrice(data) > 0 || Number(data.price_last ?? data.price ?? 0) > 0 ) } export function markPriceSourceReceived() { lastPriceSourceAt = Date.now() consecutivePriceFetchFailures = 0 } export function markPriceSourceFetchFailed() { consecutivePriceFetchFailures += 1 } export function hasRecentPriceFromSource(maxAgeMs = 45000): boolean { return lastPriceSourceAt > 0 && Date.now() - lastPriceSourceAt < maxAgeMs } export function consecutivePriceSourceFailures(): number { return consecutivePriceFetchFailures } /** buy/sell نمایش و معامله — همان buy/sell/price_last از /api/price/last */ export function getTradeBuyPrice(p?: Price | null) { return p?.buy ?? p?.price_last ?? p?.up?.buy ?? 0 } export function getTradeSellPrice(p?: Price | null) { return p?.sell ?? p?.up?.sell ?? 0 } /** up جدا نگه داشته می‌شود؛ buy/sell/price_last از lastPrice سرور دست‌نخورده می‌ماند */ export function normalizePriceResponse(data: Price): Price { return data } function startPollingInterval() { if (lastInterval || !shouldUsePricePolling()) { return } lastInterval = setInterval(() => { if (shouldUsePricePolling()) { void getLastPrices() } }, LAST_INTERVAL_MS) } function stopPollingInterval() { if (lastInterval) { clearInterval(lastInterval) lastInterval = undefined } } function syncLastPricePolling() { const usePolling = shouldUsePricePolling() if (usePolling) { if (lastSubscribers > 0) { startPollingInterval() } return } stopPollingInterval() } watch( () => [priceWssEnabled.value, priceSocketConnected.value, priceStreamLifecycle.value] as const, () => { syncLastPricePolling() }, ) /** polling /api/price/last — وقتی wss خاموش است یا ws قطع شده */ export function startLastPricePolling() { lastSubscribers++ syncLastPricePolling() } /** @deprecated از startLastPricePolling استفاده کنید */ export async function setIntervalGetAllPrice() { startLastPricePolling() } /** @deprecated از startLastPricePolling استفاده کنید */ export async function setIntervalGetLastPrice() { startLastPricePolling() } export function stopLastPricePolling() { lastSubscribers = Math.max(0, lastSubscribers - 1) if (lastSubscribers === 0) { stopPollingInterval() } } /** @deprecated از stopLastPricePolling استفاده کنید */ export function stopPriceInterval() { stopLastPricePolling() } /** @deprecated فقط getLastPrices را صدا بزنید */ export async function getUpPrice() { await getLastPrices() } async function fetchUpPrices(): Promise { try { const up = await getPrice() if (up?.buy != null && up?.sell != null) { return { buy: up.buy, sell: up.sell } } } catch (e) { console.log(e) } } export async function getLastPrices(): Promise { try { let dataLast = await getLastPrice() if (!dataLast?.up) { const up = await fetchUpPrices() if (up) { dataLast = { ...dataLast, up } } } applyLastPricePayload(dataLast) return hasValidPricePayload(dataLast) } catch (e) { console.log(e) markPriceSourceFetchFailed() return false } } export function applyLastPricePayload(dataLast: Price) { const normalized = normalizePriceResponse(dataLast) if (lastPrice.value?.price_last != normalized.price_last) { olderPrice.value = lastPrice.value?.price_last } lastPrice.value = normalized price.value = { ...normalized, buy: getTradeBuyPrice(normalized), sell: getTradeSellPrice(normalized), } if (hasValidPricePayload(normalized)) { markPriceSourceReceived() } } export function percentPriceChanged() { if (!olderPrice.value) { return 0 } return ((lastPrice.value?.price_last! - olderPrice.value) * 100) / olderPrice.value }