import { useState, useEffect, useCallback } from 'react';
import { IMAGE_BASE_URL, API_BASE_URL } from '~/lib/api';
import { logger } from '~/lib/logger';
import { isValidLanguage, loadAvailableLanguages } from '~/config/languages';

type Language = string;

interface LanguageData {
  [key: string]: any;
}

interface CachedTranslation {
  version: number;
  translations: LanguageData;
  timestamp: number;
}

const LANGUAGE_STORAGE_KEY = 'nomadic-language';
const TRANSLATION_CACHE_PREFIX = 'nomadic-translations-';
const DEFAULT_LANGUAGE: Language = 'en';

// Replace hardcoded URLs in translation data with API constants
const replaceUrlsInTranslations = (data: any): any => {
  if (typeof data === 'string') {
    // Replace hardcoded URLs with constants
    let result = data
      .replace(/https:\/\/nomadicstudios\.net:444/g, IMAGE_BASE_URL)
      .replace(/https:\/\/dashboard\.nomadicstudios\.net/g, IMAGE_BASE_URL);
    
    // Also handle cases where the string starts with /images but needs the base URL
    // If it's just a path starting with /images and doesn't already have a protocol
    if (result.startsWith('/images') && !result.includes('http')) {
      result = IMAGE_BASE_URL + result;
    }
    
    return result;
  } else if (Array.isArray(data)) {
    return data.map(replaceUrlsInTranslations);
  } else if (data && typeof data === 'object') {
    const result: any = {};
    for (const [key, value] of Object.entries(data)) {
      result[key] = replaceUrlsInTranslations(value);
    }
    return result;
  }
  return data;
};

const getCachedTranslation = (lang: Language): CachedTranslation | null => {
  if (typeof window === 'undefined') return null;
  try {
    const raw = localStorage.getItem(TRANSLATION_CACHE_PREFIX + lang);
    if (!raw) return null;
    return JSON.parse(raw) as CachedTranslation;
  } catch {
    return null;
  }
};

const setCachedTranslation = (lang: Language, version: number, translations: LanguageData): void => {
  if (typeof window === 'undefined') return;
  try {
    const entry: CachedTranslation = { version, translations, timestamp: Date.now() };
    localStorage.setItem(TRANSLATION_CACHE_PREFIX + lang, JSON.stringify(entry));
  } catch (e) {
    logger.warn('Failed to cache translations:', e);
  }
};

const CACHE_TTL_MS = 60 * 1000; // 1 minute — skip API if cache is fresher than this

const inflight = new Map<Language, Promise<{ version: number; translations: LanguageData } | null>>();

const fetchTranslationsFromAPI = (
  lang: Language
): Promise<{ version: number; translations: LanguageData } | null> => {
  const existing = inflight.get(lang);
  if (existing) return existing;

  const request = (async () => {
    try {
      const res = await fetch(`${API_BASE_URL}/translations/${lang}`, {
        method: 'GET',
        headers: { Accept: 'application/json' },
      });
      if (!res.ok) return null;
      const data = await res.json();
      if (data && typeof data.version !== 'undefined' && data.translations) {
        return { version: Number(data.version), translations: data.translations };
      }
      return null;
    } catch {
      return null;
    } finally {
      inflight.delete(lang);
    }
  })();

  inflight.set(lang, request);
  return request;
};

const isCacheFresh = (cached: CachedTranslation | null): boolean => {
  if (!cached) return false;
  return Date.now() - cached.timestamp < CACHE_TTL_MS;
};

const isPlainObject = (value: unknown): value is Record<string, unknown> => {
  return (
    !!value &&
    typeof value === 'object' &&
    !Array.isArray(value) &&
    Object.prototype.toString.call(value) === '[object Object]'
  );
};

const deepMergeTranslations = (base: any, overlay: any): any => {
  if (!isPlainObject(base) || !isPlainObject(overlay)) {
    return overlay ?? base;
  }

  const result: Record<string, unknown> = { ...base };

  for (const [key, overlayValue] of Object.entries(overlay)) {
    const baseValue = (base as Record<string, unknown>)[key];

    if (Array.isArray(baseValue) || Array.isArray(overlayValue)) {
      result[key] = overlayValue ?? baseValue;
      continue;
    }

    if (isPlainObject(baseValue) && isPlainObject(overlayValue)) {
      result[key] = deepMergeTranslations(baseValue, overlayValue);
      continue;
    }

    result[key] = overlayValue ?? baseValue;
  }

  return result;
};

export function useLanguage() {
  const [currentLanguage, setCurrentLanguage] = useState<Language>(DEFAULT_LANGUAGE);
  const [translations, setTranslations] = useState<LanguageData>({});
  const [loading, setLoading] = useState(true);

  // Load saved language from localStorage immediately on mount
  useEffect(() => {
    if (typeof window !== 'undefined') {
      const savedLanguage = localStorage.getItem(LANGUAGE_STORAGE_KEY);
      if (savedLanguage) {
        setCurrentLanguage(savedLanguage);
      }
      loadAvailableLanguages();
    }
  }, []);

  // Load translations when language changes
  useEffect(() => {
    let cancelled = false;

    const applyTranslations = (raw: LanguageData) => {
      if (cancelled) return;
      const processed = replaceUrlsInTranslations(raw);
      setTranslations(processed);
      setLoading(false);
    };

    const loadTranslations = async () => {
      setLoading(true);
      logger.log(`Loading translations for language: ${currentLanguage}`);

      // 1. Try cached translations for instant render
      const cached = getCachedTranslation(currentLanguage);
      if (cached) {
        logger.log(`Using cached translations (v${cached.version}) for ${currentLanguage}`);
        applyTranslations(cached.translations);

        // If cache is fresh, skip API call entirely
        if (isCacheFresh(cached)) {
          logger.log(`Cache is fresh, skipping API check for ${currentLanguage}`);
          return;
        }
      }

      // 2. Fetch from API to check for updates (deduplicated across hook instances)
      const apiResult = await fetchTranslationsFromAPI(currentLanguage);

      if (apiResult) {
        const needsUpdate = !cached || cached.version !== apiResult.version;
        if (needsUpdate) {
          logger.log(
            `Translation update: ${cached ? `v${cached.version} → v${apiResult.version}` : `v${apiResult.version} (first fetch)`} for ${currentLanguage}`
          );
          setCachedTranslation(currentLanguage, apiResult.version, apiResult.translations);
          applyTranslations(apiResult.translations);
        } else {
          logger.log(`Translations up-to-date (v${apiResult.version}) for ${currentLanguage}`);
          if (!cached) applyTranslations(apiResult.translations);
        }
        return;
      }

      // 3. API unreachable – fall back to cache (already applied) or bundled JSON
      if (cached) {
        logger.log(`API unreachable, using cached translations for ${currentLanguage}`);
        return;
      }

      logger.warn(`API unreachable and no cache for ${currentLanguage}`);
      setLoading(false);
    };

    loadTranslations();
    return () => { cancelled = true; };
  }, [currentLanguage]);

  // Change language function
  const changeLanguage = useCallback((newLanguage: Language) => {
    if (newLanguage === currentLanguage) {
      logger.log(`Language already set to ${newLanguage}, skipping change`);
      return; // Avoid unnecessary changes
    }
    
    logger.log(`Changing language from ${currentLanguage} to ${newLanguage}`);
    setCurrentLanguage(newLanguage);
    if (typeof window !== 'undefined') {
      localStorage.setItem(LANGUAGE_STORAGE_KEY, newLanguage);
      // Reload the page after changing language
      window.location.reload();
    }
  }, [currentLanguage]);

  // Get translation by key with dot notation support
  const t = useCallback((key: string, defaultValue?: string): string => {
    if (!translations || Object.keys(translations).length === 0) {
      return defaultValue || key;
    }
    
    const keys = key.split('.');
    let value = translations;
    
    for (const k of keys) {
      if (value && typeof value === 'object' && k in value) {
        value = value[k];
      } else {
        return defaultValue || key;
      }
    }
    
    return typeof value === 'string' ? value : defaultValue || key;
  }, [translations]);

  // Get nested object by key
  const getSection = useCallback((key: string): any => {
    if (!translations || Object.keys(translations).length === 0) {
      return {};
    }
    
    const keys = key.split('.');
    let value = translations;
    
    for (const k of keys) {
      if (value && typeof value === 'object' && k in value) {
        value = value[k];
      } else {
        return {};
      }
    }
    
    return value || {};
  }, [translations]);

  return {
    currentLanguage,
    changeLanguage,
    t,
    getSection,
    loading,
    translations
  };
}

export type { Language };
