import { ref, watch } from 'vue' import { getAllPrices } from '@/api/home' import type { Product } from '@/models/products' import { getProductsCatalog } from '@/composables/useProductsCatalog' import { getHomeLayoutId } from '../layouts/registry' import { getProductBuyPrice, resolvePriceOffsetRatio } from '@/utils/productPrice' import { isKatodMesGroup, isMaftolMesGroup } from '@/utils/productDealMethod' import { getTradeBuyPrice, lastPrice } from '@/utils/price' export type ChartMetalType = 'katod' | 'maftol' type ChartPoint = [number, number] /** Module-level chart state — one dataset shared by mobile and desktop PriceChart instances. */ const priceChartSeries = ref<{ name: string; data: ChartPoint[] }[]>([ { name: 'قیمت', data: [] }, ]) const rawChartPoints = ref([]) const chartSelection = ref('DAILY') const chartLoading = ref(false) const selectedChartMetal = ref('katod') const maftolChartRatio = ref(0) let chartFetchPromise: Promise | null = null let chartInitialized = false let maftolOffsetPromise: Promise | null = null let maftolOffsetLoaded = false function findKatodProduct(products: Product[]) { return ( products.find((product) => isKatodMesGroup(product.group_type)) ?? products.find((product) => (product.title ?? '').includes('کاتد')) ) } function findMaftolProduct(products: Product[]) { return ( products.find((product) => isMaftolMesGroup(product.group_type)) ?? products.find((product) => (product.title ?? '').includes('مفتول')) ) } function applyChartRatio(basePrice: number, ratio: number) { if (!ratio) { return basePrice } return Math.round(basePrice + basePrice * ratio) } function deriveMaftolChartRatio(maftolProduct?: Product, katodProduct?: Product) { const offsetRatio = resolvePriceOffsetRatio(maftolProduct?.price_offset_percent) if (offsetRatio) { return offsetRatio } if (!maftolProduct) { return 0 } const basePrice = getTradeBuyPrice(lastPrice.value) const katodPrice = katodProduct ? getProductBuyPrice(katodProduct) : basePrice const maftolPrice = getProductBuyPrice(maftolProduct) const referencePrice = katodPrice || basePrice if (!referencePrice || !maftolPrice || referencePrice === maftolPrice) { return 0 } return (maftolPrice - referencePrice) / referencePrice } function normalizeChartTimestamp(timestamp: number) { return timestamp < 1_000_000_000_000 ? timestamp * 1000 : timestamp } function resolvePricesMap(response: unknown): Record> | null { if (!response || typeof response !== 'object') { return null } const body = response as Record const nested = body.data if (nested && typeof nested === 'object' && 'prices' in nested) { return (nested as { prices: Record> }).prices } if ('prices' in body) { return body.prices as Record> } return null } function buildChartPoints(items: Array<{ timestamp: number; value: number }>): ChartPoint[] { return items.map((item) => [ normalizeChartTimestamp(item.timestamp), item.value - 1, ]) } function syncChartSeries() { const data = getHomeLayoutId() === 'boursecu' && selectedChartMetal.value === 'maftol' ? rawChartPoints.value.map( ([timestamp, value]) => [timestamp, applyChartRatio(value, maftolChartRatio.value)] as ChartPoint ) : rawChartPoints.value priceChartSeries.value[0].data = data } watch([selectedChartMetal, maftolChartRatio], () => { syncChartSeries() }) async function fetchPrices(force = false) { if (!force && chartInitialized && rawChartPoints.value.length > 0) { syncChartSeries() return } if (!force && chartFetchPromise) { return chartFetchPromise } chartFetchPromise = (async () => { try { chartLoading.value = true const response = await getAllPrices() const data = resolvePricesMap(response) const selectedData = data?.[chartSelection.value] ?? [] rawChartPoints.value = buildChartPoints(selectedData) syncChartSeries() chartInitialized = true } catch (error) { console.error('Error fetching prices:', error) } finally { chartLoading.value = false chartFetchPromise = null } })() return chartFetchPromise } async function loadMaftolOffset(force = false) { if (getHomeLayoutId() !== 'boursecu') { maftolChartRatio.value = 0 syncChartSeries() return } if (!force && maftolOffsetLoaded) { return } if (!force && maftolOffsetPromise) { return maftolOffsetPromise } maftolOffsetPromise = (async () => { try { const products = await getProductsCatalog(force) const katodProduct = findKatodProduct(products) const maftolProduct = findMaftolProduct(products) maftolChartRatio.value = deriveMaftolChartRatio(maftolProduct, katodProduct) syncChartSeries() maftolOffsetLoaded = true } catch (error) { console.error('Error loading maftol chart ratio:', error) maftolChartRatio.value = 0 syncChartSeries() } finally { maftolOffsetPromise = null } })() return maftolOffsetPromise } function updateChartData(timeline: string) { chartSelection.value = timeline void fetchPrices(true) } function setChartMetal(metal: ChartMetalType) { selectedChartMetal.value = metal } export function usePriceChart() { return { priceChartSeries, chartSelection, chartLoading, selectedChartMetal, maftolChartRatio, updateChartData, fetchPrices, loadMaftolOffset, setChartMetal, } } export function fetchHomeChartPrices(force = false) { return fetchPrices(force) } export function loadHomeMaftolOffset(force = false) { return loadMaftolOffset(force) } export function resetHomePriceChart() { priceChartSeries.value = [{ name: 'قیمت', data: [] }] rawChartPoints.value = [] chartSelection.value = 'DAILY' selectedChartMetal.value = 'katod' maftolChartRatio.value = 0 chartInitialized = false chartFetchPromise = null maftolOffsetLoaded = false maftolOffsetPromise = null }