import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, InternalAxiosRequestConfig } from 'axios';
import {
  SearchResponse,
  ClubInfo,
  Competition,
  SearchResult,
} from './types';

// Cache interface
interface CacheItem<T> {
  data: T;
  timestamp: number;
}

// Cache store
const cache = new Map<string, CacheItem<any>>();

// Create axios instance with base URL from environment variables
const apiClient: AxiosInstance = axios.create({
  baseURL: process.env.REACT_APP_FACR_API_BASE_URL || 'http://localhost:8080/api/v1/facr',
  timeout: parseInt(process.env.REACT_APP_FACR_API_TIMEOUT || '20000', 10),
  headers: {
    'Content-Type': 'application/json',
    'Accept': 'application/json',
  },
});

// Helper: resolve backend absolute URL for /api and /cache paths
const resolveBackendUrl = (path: string) => {
  try {
    if (/^https?:\/\//i.test(path)) return path;
    if (path.startsWith('/cache') || path.startsWith('/uploads') || path.startsWith('/api/')) {
      const base = process.env.REACT_APP_API_BASE_URL || process.env.REACT_APP_API_URL || 'http://localhost:8080/api/v1';
      const baseOrigin = new URL(base).origin;
      // Use URL constructor so query strings in `path` (e.g. /api/v1/x?t=123) are handled correctly
      return new URL(path, baseOrigin).toString();
    }
    return path;
  } catch {
    return path;
  }
};

// Lazy-load public overrides with lightweight cache
let overridesCache: { data: any; ts: number } | null = null;
const loadOverrides = async (): Promise<Record<string, string>> => {
  const now = Date.now();
  if (overridesCache && now - overridesCache.ts < 60_000) {
    return (overridesCache.data?.by_name || {}) as Record<string, string>;
  }
  try {
    const res = await fetch(resolveBackendUrl(`/api/v1/public/team-logo-overrides?t=${now}`), { cache: 'no-cache' });
    if (res.ok) {
      const json = await res.json();
      const prev = JSON.stringify(overridesCache?.data || {});
      const next = JSON.stringify(json || {});
      overridesCache = { data: json, ts: now };
      if (prev !== next) {
        // Invalidate internal FACR GET cache so consumers refetch with new logos
        cache.clear();
      }
      return (json?.by_name || {}) as Record<string, string>;
    }
  } catch {}
  // Fallback to cached file if API failed
  try {
    const res2 = await fetch(resolveBackendUrl('/cache/prefetch/team_logo_overrides.json'), { cache: 'no-cache' });
    if (res2.ok) {
      const json = await res2.json();
      const prev = JSON.stringify(overridesCache?.data || {});
      const next = JSON.stringify(json || {});
      overridesCache = { data: json, ts: now };
      if (prev !== next) {
        cache.clear();
      }
      return (json?.by_name || {}) as Record<string, string>;
    }
  } catch {}
  overridesCache = { data: { by_name: {} }, ts: now };
  return {};
};

// Name normalization helpers
const norm = (s: string) => String(s || '')
  .normalize('NFD')
  .replace(/[\u0300-\u036f]/g, '')
  .replace(/\s+/g, ' ')
  .trim()
  .toLowerCase();
const stripPrefixes = (s: string) => {
  let x = norm(s);
  // Remove common Czech tokens/prefixes
  x = x.replace(/\b(mestsky|m\.?f\.?k\.?|mfk|tj|sk|sokol|fotbalovy|fotbalový|fotbalovy\s+klub|fotbalovy\s+klub)\b/g, '');
  return x.replace(/\s+/g, ' ').trim();
};
const applyOverridesToClub = (club: ClubInfo, byName: Record<string, string>) => {
  if (!club?.competitions?.length) return club;
  const byNameNorm: Record<string, string> = Object.keys(byName || {}).reduce((acc: Record<string, string>, k) => {
    acc[norm(k)] = byName[k];
    return acc;
  }, {});
  const strippedPairs = Object.keys(byName || {}).map((k) => ({ key: stripPrefixes(k), url: byName[k] }));
  const pick = (teamName?: string, original?: string) => {
    if (!teamName) return original;
    const exact = (byName || {})[teamName];
    const n = norm(teamName);
    let candidate = exact || byNameNorm[n];
    if (!candidate) {
      const s = stripPrefixes(teamName);
      for (const { key, url } of strippedPairs) {
        if (!key) continue;
        if (s.endsWith(key) || key.endsWith(s)) { candidate = url; break; }
      }
    }
    const chosen = candidate || original;
    if (typeof chosen === 'string' && chosen.startsWith('/')) return resolveBackendUrl(chosen);
    return chosen;
  };
  club.competitions = (club.competitions || []).map((c) => ({
    ...c,
    matches: (c.matches || []).map((m: any) => ({
      ...m,
      home_logo_url: pick(m.home, m.home_logo_url),
      away_logo_url: pick(m.away, m.away_logo_url),
    })),
  }));
  return club;
};

// Request interceptor for caching
apiClient.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    // Skip cache for non-GET requests
    if (config.method?.toLowerCase() !== 'get') {
      return config;
    }

    // Generate a cache key based on the request
    const cacheKey = `${config.method}:${config.url}:${JSON.stringify(config.params)}`;
    
    // Check if we have a cached response
    const cached = cache.get(cacheKey);
    const now = Date.now();
    const cacheTtl = parseInt(process.env.REACT_APP_FACR_CACHE_TTL || '3600000', 10);
    
    if (cached && now - cached.timestamp < cacheTtl) {
      // Return cached response
      return {
        ...config,
        adapter: () => Promise.resolve({
          data: cached.data,
          status: 200,
          statusText: 'OK',
          headers: {},
          config,
        }),
      };
    }
    
    return config;
  },
  (error) => {
    return Promise.reject(error);
  }
);

// Response interceptor for caching successful responses
apiClient.interceptors.response.use(
  (response: AxiosResponse) => {
    const method = response.config.method?.toLowerCase();
    const url = response.config.url;
    const params = response.config.params;
    
    // Only cache GET requests
    if (method === 'get') {
      const cacheKey = `${method}:${url}:${JSON.stringify(params)}`;
      cache.set(cacheKey, {
        data: response.data,
        timestamp: Date.now(),
      });
    }
    return response;
  },
  (error) => {
    console.error('API Error:', error);
    return Promise.reject(error);
  }
);

// Helper function to handle API errors
const handleApiError = (error: any) => {
  if (error.response) {
    // The request was made and the server responded with a status code
    // that falls out of the range of 2xx
    const errorData = error.response.data || {};
    const errorMessage = errorData.message || 'API request failed';
    console.error('API Error Response:', errorData);
    throw new Error(errorMessage);
  } else if (error.request) {
    // The request was made but no response was received
    console.error('API Error: No response received', error.request);
    throw new Error('No response from server. Please check if the FACR scraper API is running.');
  } else {
    // Something happened in setting up the request that triggered an Error
    console.error('API Error:', error.message);
    throw new Error(`API request failed: ${error.message}`);
  }
};

// FACR API client
export const facrApi = {
  // Search for clubs
  searchClubs: async (query: string): Promise<SearchResponse> => {
    try {
      const response = await apiClient.get<SearchResponse>('/club/search', {
        params: { q: query },
      });
      return response.data;
    } catch (error) {
      return handleApiError(error);
    }
  },

  // Get club details and matches
  getClub: async (clubId: string, clubType: 'football' | 'futsal' = 'football'): Promise<ClubInfo> => {
    try {
      const response = await apiClient.get<ClubInfo>(`/club/${clubType}/${clubId}`);
      // Load overrides and apply before returning/caching consumers
      const byName = await loadOverrides();
      const patched = applyOverridesToClub(response.data, byName);
      return patched;
    } catch (error) {
      return handleApiError(error);
    }
  },

  // Get club table (standings)
  getClubTable: async (clubId: string, clubType: 'football' | 'futsal' = 'football'): Promise<ClubInfo> => {
    try {
      const response = await apiClient.get<ClubInfo>(`/club/${clubType}/${clubId}/table`);
      return response.data;
    } catch (error) {
      return handleApiError(error);
    }
  },

  // Clear cache
  clearCache: (): void => {
    cache.clear();
  },

  // Get all competitions for a club
  getClubCompetitions: async (clubId: string, clubType: 'football' | 'futsal' = 'football'): Promise<Competition[]> => {
    try {
      const clubInfo = await facrApi.getClub(clubId, clubType);
      return clubInfo.competitions || [];
    } catch (error) {
      return handleApiError(error);
    }
  },

  // Get matches for a specific competition
  getCompetitionMatches: async (competitionId: string): Promise<any[]> => {
    try {
      // Note: This assumes the competition ID is in the format 'type/id'
      const [type, id] = competitionId.split('/');
      const response = await apiClient.get<ClubInfo>(`/club/${type}/${id}`);
      // Find the specific competition and return its matches
      const competition = response.data.competitions?.find(c => c.id === competitionId);
      return competition?.matches || [];
    } catch (error) {
      return handleApiError(error);
    }
  },
};

export default facrApi;
