import { API_CONFIG, API_ENDPOINTS } from "@/constants/api"; import { ApiListResponse, ApiResponse, AppleSignInRequest, AppleSignInResponse, Country, FavoriteCheckResponse, FavoriteListResponse, FavoriteRequest, H2HData, League, LiveScoreMatch, Match, MatchDetailData, OddsData, RefreshTokenRequest, RefreshTokenResponse, SearchResult, Sport, UpcomingMatch, UserProfile, } from "@/types/api"; import axios from "axios"; import { storage } from "./storage"; const apiClient = axios.create({ baseURL: API_CONFIG.BASE_URL, timeout: API_CONFIG.TIMEOUT, headers: { "Content-Type": "application/json", }, }); apiClient.interceptors.request.use(async (config) => { const token = await storage.getAccessToken(); if (token) { config.headers.Authorization = `Bearer ${token}`; } return config; }); const refreshTokenApi = async ( request: RefreshTokenRequest, ): Promise => { try { const response = await apiClient.post>( API_ENDPOINTS.REFRESH_TOKEN, request, ); if (response.data.code === 0) { return response.data.data; } throw new Error(response.data.message); } catch (error) { console.error("Refresh token error:", error); throw error; } }; apiClient.interceptors.response.use( (response) => response, async (error) => { const originalRequest = error.config; if (error.response?.status === 401 && !originalRequest._retry) { originalRequest._retry = true; try { const refreshTokenValue = await storage.getRefreshToken(); if (refreshTokenValue) { const res = await refreshTokenApi({ refreshToken: refreshTokenValue, }); await storage.setAccessToken(res.accessToken); originalRequest.headers.Authorization = `Bearer ${res.accessToken}`; return apiClient(originalRequest); } } catch { await storage.clear(); } } return Promise.reject(error); }, ); export const fetchSports = async (): Promise => { try { const response = await apiClient.get>>( API_ENDPOINTS.SPORTS, ); if (response.data.code === 0) { return response.data.data.list; } throw new Error(response.data.message); } catch (error) { console.error("Fetch sports error:", error); // Do not return mock data here — rethrow to let caller handle the error throw error; } }; export const fetchCountries = async (): Promise => { try { const response = await apiClient.get>>( API_ENDPOINTS.COUNTRIES, ); if (response.data.code === 0) { return response.data.data.list; } throw new Error(response.data.message); } catch (error) { console.error("Fetch countries error:", error); throw error; } }; export const fetchLeagues = async (params: { sportId?: number; countryKey?: string; date?: string; page?: number; pageSize?: number; sortBy?: "name" | "matchCount" | "createdAt"; sortOrder?: "asc" | "desc"; }): Promise> => { try { const response = await apiClient.get>>( API_ENDPOINTS.LEAGUES, { params }, ); if (response.data.code === 0) { return response.data.data; } throw new Error(response.data.message); } catch (error) { console.error("Fetch leagues error:", error); throw error; } }; export const fetchTodayMatches = async (options: { sportId: number; date?: Date | string; timezone?: string; leagueKey?: string; page?: number; pageSize?: number; }): Promise> => { try { const params: { sportId: number; date?: string; timezone?: string; leagueKey?: string; page?: number; pageSize?: number; } = { sportId: options.sportId, leagueKey: options.leagueKey, page: options.page, pageSize: options.pageSize, }; if (options.date) { if (options.date instanceof Date) { const year = options.date.getFullYear(); const month = String(options.date.getMonth() + 1).padStart(2, "0"); const day = String(options.date.getDate()).padStart(2, "0"); params.date = `${year}-${month}-${day}`; } else { params.date = options.date; } } if (options.timezone) { params.timezone = options.timezone; } const response = await apiClient.get>>( API_ENDPOINTS.MATCHES_TODAY, { params }, ); if (response.data.code === 0) { return response.data.data; } throw new Error(response.data.message); } catch (error) { console.error("Fetch matches error:", error); throw error; } }; export const fetchMatchDetail = async ( id: string, ): Promise => { try { const response = await apiClient.get>( API_ENDPOINTS.MATCH_DETAIL(id), ); if (response.data.code === 0) { return response.data.data; } throw new Error(response.data.message); } catch (error) { console.error("Fetch match detail error:", error); throw error; } }; export const fetchLiveScore = async ( sportId: number, leagueId?: number, timezone?: string, ): Promise => { // console.log("Fetching live scores with params:", { // sportId, // leagueId, // timezone, // }); try { const params: { sport_id: number; league_id?: number; timezone?: string } = { sport_id: sportId, }; if (leagueId) { params.league_id = leagueId; } if (timezone) { params.timezone = timezone; } const response = await apiClient.get>( API_ENDPOINTS.LIVESCORE, { params }, ); if (response.data.code === 0) { // console.log("Live score data:", JSON.stringify(response.data.data)); return response.data.data; } throw new Error(response.data.message); } catch (error) { console.error("Fetch livescore error:", error); throw error; } }; export const fetchUpcomingMatches = async ( sportId: number, leagueKey: string, limit: number = 50, ): Promise => { try { const response = await apiClient.get< ApiResponse> >(API_ENDPOINTS.UPCOMING_MATCHES, { params: { sport_id: sportId, leagueKey, limit, }, }); if (response.data.code === 0) { return response.data.data.list; } throw new Error(response.data.message); } catch (error) { console.error("Fetch upcoming matches error:", error); throw error; } }; // 获取实时赔率(足球/网球使用 LiveOdds,篮球/板球使用 Odds) export const fetchOdds = async ( sportId: number, matchId: number, ): Promise => { try { const response = await apiClient.get>( API_ENDPOINTS.ODDS, { params: { sport_id: sportId, match_id: matchId, }, }, ); if (response.data.code === 0) { return response.data.data; } throw new Error(response.data.message); } catch (error) { console.error("Fetch odds error:", error); throw error; } }; // 搜索联赛、球队或球员 export const fetchSearch = async ( query: string, sportId?: number, ): Promise => { try { const params: { q: string; sportId?: number } = { q: query }; if (sportId) { params.sportId = sportId; } const response = await apiClient.get>( API_ENDPOINTS.SEARCH, { params }, ); if (response.data.code === 0) { return response.data.data; } throw new Error(response.data.message); } catch (error) { console.error("Fetch search error:", error); throw error; } }; // 获取两队/两球员之间的历史对战数据 export const fetchH2H = async ( sportId: number, options: { firstTeamId?: number; secondTeamId?: number; firstPlayerId?: number; secondPlayerId?: number; timezone?: string; }, ): Promise => { try { const params: { sport_id: number; first_team_id?: number; second_team_id?: number; first_player_id?: number; second_player_id?: number; timezone?: string; } = { sport_id: sportId, }; if (options.firstTeamId) { params.first_team_id = options.firstTeamId; } if (options.secondTeamId) { params.second_team_id = options.secondTeamId; } if (options.firstPlayerId) { params.first_player_id = options.firstPlayerId; } if (options.secondPlayerId) { params.second_player_id = options.secondPlayerId; } if (options.timezone) { params.timezone = options.timezone; } const response = await apiClient.get>( API_ENDPOINTS.H2H, { params }, ); if (response.data.code === 0) { return response.data.data; } throw new Error(response.data.message); } catch (error) { console.error("Fetch H2H error:", error); throw error; } }; export const appleSignIn = async ( request: AppleSignInRequest, ): Promise => { try { const response = await apiClient.post>( API_ENDPOINTS.APPLE_SIGNIN, request, ); if (response.data.code === 0) { return response.data.data; } throw new Error(response.data.message); } catch (error) { console.error("Apple sign in error:", error); throw error; } }; export const logout = async (): Promise => { try { const response = await apiClient.post>( API_ENDPOINTS.LOGOUT, ); if (response.data.code === 0) { return response.data.data; } throw new Error(response.data.message); } catch (error) { console.error("Logout error:", error); throw error; } }; export const refreshToken = refreshTokenApi; export const fetchUserProfile = async (): Promise => { try { const response = await apiClient.get>( API_ENDPOINTS.USER_PROFILE, ); if (response.data.code === 0) { return response.data.data; } throw new Error(response.data.message); } catch (error) { console.error("Fetch user profile error:", error); throw error; } }; export const addFavorite = async (request: FavoriteRequest): Promise => { try { const token = await storage.getAccessToken(); if (!token) { // throw new Error("Please login first"); return; } // console.log("Adding favorite with request:", request); const response = await apiClient.post>( API_ENDPOINTS.FAVORITES, request, ); if (response.data.code === 0) { return response.data.data; } throw new Error(response.data.message); } catch (error) { console.error("Add favorite error:", error); throw error; } }; export const removeFavorite = async (request: { type: string; typeId: string; }): Promise => { try { const token = await storage.getAccessToken(); if (!token) { // throw new Error("Please login first"); return; } console.log("Removing favorite with request:", request); const response = await apiClient.delete>( API_ENDPOINTS.FAVORITES, { data: request }, ); if (response.data.code === 0) { return response.data.data; } throw new Error(response.data.message); } catch (error) { console.error("Remove favorite error:", error); throw error; } }; export const checkFavorite = async ( type: string, typeId: string, ): Promise => { try { const response = await apiClient.get>( API_ENDPOINTS.CHECK_FAVORITE, { params: { type, typeId }, }, ); if (response.data.code === 0) { return response.data.data; } throw new Error(response.data.message); } catch (error) { console.error("Check favorite error:", error); throw error; } }; export const fetchFavorites = async ( type: string, page: number = 1, pageSize: number = 20, ): Promise => { try { const response = await apiClient.get>( API_ENDPOINTS.FAVORITES, { params: { type, page, pageSize }, }, ); if (response.data.code === 0) { console.log("Fetched favorites:", JSON.stringify(response.data.data)); return response.data.data; } throw new Error(response.data.message); } catch (error) { console.error("Fetch favorites error:", error); throw error; } };