import type { DateRange } from './types'; export type DateBucket = 'day' | 'week' | 'month'; const MS_PER_DAY = 24 * 60 * 60 * 1000; export const getRangeDayCount = (dateRange: DateRange) => ( Math.max(1, Math.ceil((dateRange.end.getTime() - dateRange.start.getTime()) / MS_PER_DAY) + 1) ); export const getAutoDateBucket = (dateRange: DateRange): DateBucket => { const days = getRangeDayCount(dateRange); if (days > 365) return 'month'; if (days > 90) return 'week'; return 'day'; }; export const parseChartDate = (value: string): Date | null => { if (!value) return null; const isoMatch = value.match(/^(\d{4})-(\d{2})-(\d{2})$/); if (isoMatch) { const [, year, month, day] = isoMatch; return new Date(Number(year), Number(month) - 1, Number(day)); } const localMatch = value.match(/^(\d{2})\/(\d{2})\/(\d{4})$/); if (localMatch) { const [, day, month, year] = localMatch; return new Date(Number(year), Number(month) - 1, Number(day)); } const dashedLocalMatch = value.match(/^(\d{2})-(\d{2})-(\d{4})$/); if (dashedLocalMatch) { const [, day, month, year] = dashedLocalMatch; return new Date(Number(year), Number(month) - 1, Number(day)); } const date = new Date(value); return Number.isNaN(date.getTime()) ? null : date; }; export const formatChartDateKey = (date: Date) => { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); return `${year}-${month}-${day}`; }; const startOfWeek = (date: Date) => { const nextDate = new Date(date); const day = nextDate.getDay(); const offset = day === 0 ? -6 : 1 - day; nextDate.setDate(nextDate.getDate() + offset); nextDate.setHours(0, 0, 0, 0); return nextDate; }; export const getDateBucketKey = (value: string, bucket: DateBucket) => { const date = parseChartDate(value); if (!date) return value; if (bucket === 'day') return formatChartDateKey(date); if (bucket === 'week') return formatChartDateKey(startOfWeek(date)); return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`; }; export const formatDateBucketLabel = (value: string, bucket: DateBucket) => { if (bucket === 'month') { const match = value.match(/^(\d{4})-(\d{2})$/); if (!match) return value; const [, year, month] = match; return new Intl.DateTimeFormat('pt-BR', { month: 'short', year: '2-digit' }).format(new Date(Number(year), Number(month) - 1, 1)); } const date = parseChartDate(value); if (!date) return value; if (bucket === 'week') { const endDate = new Date(date); endDate.setDate(endDate.getDate() + 6); const formatter = new Intl.DateTimeFormat('pt-BR', { day: '2-digit', month: '2-digit' }); return `${formatter.format(date)}-${formatter.format(endDate)}`; } return new Intl.DateTimeFormat('pt-BR', { day: '2-digit', month: '2-digit' }).format(date); }; export const formatDateBucketLongLabel = (value: string, bucket: DateBucket) => { if (bucket === 'month') { const match = value.match(/^(\d{4})-(\d{2})$/); if (!match) return value; const [, year, month] = match; return new Intl.DateTimeFormat('pt-BR', { month: 'long', year: 'numeric' }).format(new Date(Number(year), Number(month) - 1, 1)); } const date = parseChartDate(value); if (!date) return value; if (bucket === 'week') { const endDate = new Date(date); endDate.setDate(endDate.getDate() + 6); const formatter = new Intl.DateTimeFormat('pt-BR', { day: '2-digit', month: '2-digit', year: 'numeric' }); return `${formatter.format(date)} - ${formatter.format(endDate)}`; } return new Intl.DateTimeFormat('pt-BR', { day: '2-digit', month: '2-digit', year: 'numeric' }).format(date); }; export const getMovingAverageWindow = (bucket: DateBucket) => { if (bucket === 'day') return 7; if (bucket === 'week') return 4; return 3; }; export const averageRecentValues = (values: number[], endIndex: number, windowSize: number) => { const startIndex = Math.max(0, endIndex - windowSize + 1); const windowValues = values.slice(startIndex, endIndex + 1); if (!windowValues.length) return 0; return windowValues.reduce((total, value) => total + value, 0) / windowValues.length; }; export const normalizeUnknownLabel = (value: string, fallback: string) => { const normalized = String(value || '').replace(/\s+/g, ' ').trim(); if (!normalized) return fallback; if (/^(0|null|undefined|unknown|n\/a|-|sem nome)$/i.test(normalized)) return fallback; return normalized; };