import { assetUrl } from '../utils/url'; import { API_URL } from './api'; export interface RelatedClub { id: string; name: string; logo_url?: string; competition?: string; last_played_iso?: string; matches_played?: number; } const normalize = (value: string) => String(value || '') .normalize('NFD') .replace(/[\u0300-\u036f]/g, '') .replace(/\s+/g, ' ') .trim() .toLowerCase(); const resolveBackendUrl = (path: string): string => { try { if (/^https?:\/\//i.test(path)) return path; if (path.startsWith('/cache') || path.startsWith('/uploads') || path.startsWith('/api/')) { const origin = new URL(API_URL, typeof window !== 'undefined' ? window.location.origin : 'http://localhost:3000').origin; return new URL(path, origin).toString(); } return path; } catch { return path; } }; const fetchJSON = async (path: string): Promise => { try { const res = await fetch(resolveBackendUrl(path), { cache: 'no-cache' }); if (!res.ok) return null; return (await res.json()) as T; } catch { return null; } }; const parseDateTime = (dt?: string): Date | null => { if (!dt) return null; const [datePart, timePart = '00:00'] = String(dt).split(' '); const [day, month, year] = (datePart || '').split('.'); if (!day || !month || !year) return null; const iso = `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}T${timePart.slice(0, 5)}:00`; const d = new Date(iso); return Number.isNaN(d.getTime()) ? null : d; }; let cachePromise: Promise | null = null; const buildRelatedClubs = (clubInfo: any): RelatedClub[] => { if (!clubInfo) return []; const ourNameNorm = normalize(clubInfo.name || ''); const competitions = Array.isArray(clubInfo.competitions) ? clubInfo.competitions : []; const map = new Map(); const considerTeam = (team: { name?: string; id?: string; logo?: string; competition?: string; occurredAt?: Date | null; }) => { const teamName = team.name?.trim(); if (!teamName) return; const normName = normalize(teamName); if (!normName || normName === ourNameNorm) return; const existing = map.get(normName); const matchesPlayed = (existing?.matches_played ?? 0) + 1; const lastPlayedDate = team.occurredAt && (!existing?.lastPlayedDate || team.occurredAt > existing.lastPlayedDate) ? team.occurredAt : existing?.lastPlayedDate; const logoUrl = team.logo || existing?.logo_url; const competition = team.competition || existing?.competition; map.set(normName, { id: team.id || existing?.id || normName, name: teamName, logo_url: logoUrl ? assetUrl(logoUrl) || logoUrl : existing?.logo_url, competition, matches_played: matchesPlayed, last_played_iso: lastPlayedDate ? lastPlayedDate.toISOString() : existing?.last_played_iso, lastPlayedDate, }); }; competitions.forEach((comp: any) => { const matches = Array.isArray(comp?.matches) ? comp.matches : []; matches.forEach((match: any) => { const occurredAt = parseDateTime(match?.date_time); considerTeam({ name: match?.home, id: match?.home_id || match?.homeId, logo: match?.home_logo_url, competition: comp?.name, occurredAt, }); considerTeam({ name: match?.away, id: match?.away_id || match?.awayId, logo: match?.away_logo_url, competition: comp?.name, occurredAt, }); }); }); return Array.from(map.values()) .map(({ lastPlayedDate, ...rest }) => rest) .sort((a, b) => { const ad = a.last_played_iso ? new Date(a.last_played_iso).getTime() : 0; const bd = b.last_played_iso ? new Date(b.last_played_iso).getTime() : 0; return bd - ad; }); }; const fetchRelatedClubs = async (): Promise => { const clubInfo = await fetchJSON('/cache/prefetch/facr_club_info.json'); if (!clubInfo) return []; return buildRelatedClubs(clubInfo); }; export const getRelatedClubs = async (): Promise => { if (!cachePromise) { cachePromise = fetchRelatedClubs().catch(() => []); } return cachePromise; };