/**
 * 네트워크 유틸리티 모듈
 * 
 * 이 파일은 하위 모듈로 리팩토링되었습니다.
 * 기존 코드와의 호환성을 위해 새 모듈에서 모든 기능을 재내보냅니다.
 */

// 모든 네트워크 유틸리티 기능을 새 모듈에서 가져와 재내보내기
export * from './network';

/**
 * 네트워크 상태 관리 및 오류 처리를 위한 유틸리티
 */

// 네트워크 상태 저장 키
const NETWORK_STATUS_KEY = 'network_status';

// 네트워크 재시도 설정
const MAX_RETRY_COUNT = 3;
const INITIAL_RETRY_DELAY = 1000; // 1초

// 네트워크 재연결 시도 설정
const RECONNECT_INTERVAL = 5000; // 5초마다 재연결 시도
const MAX_RECONNECT_ATTEMPTS = 10; // 최대 재연결 시도 횟수
let reconnectAttempts = 0;
let reconnectTimer: ReturnType<typeof setTimeout> | null = null;

// 네트워크 모니터링 상태
let isMonitoring = false;
let networkCheckInterval: ReturnType<typeof setInterval> | null = null;

// 네트워크 상태 타입
export type NetworkStatus = 'online' | 'offline' | 'reconnecting';

// 동기화 작업 큐 아이템 타입
export interface SyncQueueItem {
  id: string;
  type: 'upload' | 'download' | 'delete';
  entityType: 'transaction' | 'budget' | 'categoryBudget';
  data: Record<string, unknown>;
  timestamp: number;
  retryCount: number;
}

// 네트워크 이벤트 핸들러 인터페이스
interface NetworkEventHandlers {
  handleOnline: () => void;
  handleOffline: () => void;
}

// 확장된 Window 인터페이스
interface ExtendedWindow extends Window {
  __networkHandlers?: NetworkEventHandlers;
}

// 메모리에 네트워크 상태 저장
let networkStatus: NetworkStatus | null = null;

/**
 * 현재 네트워크 상태 확인 (개선 버전)
 */
export const checkNetworkStatus = async (): Promise<boolean> => {
  try {
    // 더 빠른 타임아웃 설정 (2초)
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 2000);
    
    // 여러 엔드포인트로 확인하여 신뢰성 향상
    const endpoints = [
      'https://www.google.com',
      'https://www.cloudflare.com',
      'https://www.apple.com'
    ];
    
    try {
      // 각 엔드포인트에 대해 fetch 요청 시도
      const fetchPromises = endpoints.map(url => 
        fetch(url, { method: 'HEAD', signal: controller.signal })
          .then(res => {
            if (res.ok) return true;
            throw new Error(`Failed to fetch ${url}`);
          })
          .catch(err => {
            console.log(`[네트워크] ${url} 연결 실패:`, err.message);
            return false; // 실패 시 false 반환
          })
      );
      
      // 모든 요청을 병렬로 실행하고 결과 확인
      const results = await Promise.all(fetchPromises);
      
      // 하나라도 성공했으면 온라인으로 판단
      const isOnline = results.some(result => result === true);
      
      clearTimeout(timeoutId);
      setNetworkStatus(isOnline ? 'online' : 'offline');
      return isOnline;
    } catch (error) {
      clearTimeout(timeoutId);
      console.error('[네트워크] 네트워크 확인 실패:', error);
      setNetworkStatus('offline');
      return false;
    }
  } catch (error) {
    console.error('[네트워크] 연결 확인 중 예상치 못한 오류:', error);
    setNetworkStatus('offline');
    return false;
  }
};

/**
 * 네트워크 상태 저장 (메모리 기반)
 */
export const setNetworkStatus = (status: NetworkStatus): void => {
  // 메모리에 상태 저장
  networkStatus = status;
  
  // 상태 변경 이벤트 발생
  window.dispatchEvent(new CustomEvent('networkStatusChange', { detail: status }));
  console.log(`[네트워크] 상태 변경: ${status}`);
};

/**
 * 네트워크 상태 가져오기 (메모리 기반)
 */
export const getNetworkStatus = (): NetworkStatus => {
  return networkStatus || 'online';
};

/**
 * 네트워크 상태 모니터링 시작 함수 개선
 */
export const startNetworkMonitoring = (): void => {
  if (isMonitoring) {
    return;
  }
  
  isMonitoring = true;
  
  // 초기 네트워크 상태를 온라인으로 설정 (실제 확인 전)
  setNetworkStatus('online');
  
  // 초기 네트워크 상태 확인
  checkNetworkStatus().then(isOnline => {
    if (!isOnline) {
      // 오프라인 상태면 재연결 시도 시작
      reconnectTimer = setTimeout(attemptReconnect, RECONNECT_INTERVAL);
    }
  });
  
  // 브라우저 온라인/오프라인 이벤트 리스너
  const handleOnline = () => {
    console.log('[네트워크] 브라우저 온라인 이벤트 감지');
    setNetworkStatus('online');
    reconnectAttempts = 0;
    
    // 대기 중인 동기화 작업 처리
    processPendingSyncQueue().catch(err => {
      console.error('[네트워크] 온라인 전환 후 동기화 큐 처리 중 오류:', err);
    });
  };
  
  const handleOffline = () => {
    console.log('[네트워크] 브라우저 오프라인 이벤트 감지');
    setNetworkStatus('offline');
    
    // 재연결 시도 시작
    reconnectTimer = setTimeout(attemptReconnect, RECONNECT_INTERVAL);
  };
  
  // 이벤트 리스너 등록 및 참조 저장
  window.addEventListener('online', handleOnline);
  window.addEventListener('offline', handleOffline);
  
  // 전역 변수에 이벤트 핸들러 참조 저장
  (window as ExtendedWindow).__networkHandlers = {
    handleOnline,
    handleOffline
  };
  
  // 정기적인 네트워크 상태 확인 (30초마다)
  networkCheckInterval = setInterval(async () => {
    try {
      const isOnline = await checkNetworkStatus();
      const currentStatus = getNetworkStatus();
      
      if (isOnline && currentStatus !== 'online') {
        // 오프라인에서 온라인으로 변경
        setNetworkStatus('online');
        reconnectAttempts = 0;
        
        // 대기 중인 동기화 작업 처리
        processPendingSyncQueue().catch(err => {
          console.error('[네트워크] 정기 확인 후 동기화 큐 처리 중 오류:', err);
        });
      } else if (!isOnline && currentStatus === 'online') {
        // 온라인에서 오프라인으로 변경
        setNetworkStatus('offline');
        
        // 재연결 시도 시작
        if (!reconnectTimer) {
          reconnectTimer = setTimeout(attemptReconnect, RECONNECT_INTERVAL);
        }
      }
    } catch (error) {
      console.error('[네트워크] 정기 네트워크 상태 확인 중 오류:', error);
    }
  }, 30000);
  
  console.log('[네트워크] 네트워크 모니터링 시작');
};

/**
 * 네트워크 상태 모니터링 중지
 */
export const stopNetworkMonitoring = (): void => {
  if (!isMonitoring) {
    return;
  }
  
  isMonitoring = false;
  
  // 브라우저 이벤트 리스너 제거
  const handlers = (window as ExtendedWindow).__networkHandlers;
  if (handlers) {
    window.removeEventListener('online', handlers.handleOnline);
    window.removeEventListener('offline', handlers.handleOffline);
    delete (window as ExtendedWindow).__networkHandlers;
  }
  
  // 인터벌 및 타이머 정리
  if (networkCheckInterval) {
    clearInterval(networkCheckInterval);
    networkCheckInterval = null;
  }
  
  if (reconnectTimer) {
    clearTimeout(reconnectTimer);
    reconnectTimer = null;
  }
  
  reconnectAttempts = 0;
  
  console.log('[네트워크] 네트워크 모니터링 중지');
};

/**
 * 네트워크 재연결 시도 함수
 */
export const attemptReconnect = async (): Promise<boolean> => {
  if (getNetworkStatus() === 'online') {
    // 이미 온라인 상태면 재연결 필요 없음
    reconnectAttempts = 0;
    if (reconnectTimer) {
      clearTimeout(reconnectTimer);
      reconnectTimer = null;
    }
    return true;
  }

  // 재연결 시도 중 상태로 변경
  setNetworkStatus('reconnecting');
  
  try {
    const isOnline = await checkNetworkStatus();
    if (isOnline) {
      // 재연결 성공
      setNetworkStatus('online');
      reconnectAttempts = 0;
      console.log('[네트워크] 재연결 성공');
      
      // 대기 중인 동기화 작업 처리
      processPendingSyncQueue().catch(err => {
        console.error('[네트워크] 재연결 후 동기화 큐 처리 중 오류:', err);
      });
      
      return true;
    } else {
      // 재연결 실패
      reconnectAttempts++;
      console.log(`[네트워크] 재연결 실패 (시도 ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS})`);
      
      if (reconnectAttempts >= MAX_RECONNECT_ATTEMPTS) {
        // 최대 재연결 시도 횟수 초과
        setNetworkStatus('offline');
        reconnectAttempts = 0;
        console.log('[네트워크] 최대 재연결 시도 횟수 초과, 오프라인 모드로 전환');
        return false;
      }
      
      // 다음 재연결 시도 예약
      reconnectTimer = setTimeout(attemptReconnect, RECONNECT_INTERVAL);
      return false;
    }
  } catch (error) {
    console.error('[네트워크] 재연결 시도 중 오류:', error);
    setNetworkStatus('offline');
    reconnectAttempts = 0;
    return false;
  }
};

/**
 * 온라인 상태 처리
 */
const handleOnline = async () => {
  console.log('[네트워크] 온라인 상태 감지');
  setNetworkStatus('reconnecting');
  
  // 실제 연결 확인 (브라우저 이벤트는 때때로 부정확할 수 있음)
  const isReallyOnline = await checkNetworkStatus();
  
  if (isReallyOnline) {
    console.log('[네트워크] 온라인 상태 확인됨, 보류 중인 작업 처리 시작');
    processPendingSyncQueue();
  }
};

/**
 * 오프라인 상태 처리
 */
const handleOffline = () => {
  console.log('[네트워크] 오프라인 상태 감지');
  setNetworkStatus('offline');
};

// 동기화 작업 큐 관리
const SYNC_QUEUE_KEY = 'sync_queue';

/**
 * 동기화 작업을 큐에 추가
 */
export const addToSyncQueue = (operation: {
  type: 'upload' | 'download' | 'delete';
  entityType: 'transaction' | 'budget' | 'categoryBudget';
  data: Record<string, unknown>;
  timestamp?: number;
  retries?: number;
}): void => {
  // 타임스탬프 및 재시도 횟수 추가
  const enhancedOperation = {
    ...operation,
    timestamp: operation.timestamp || Date.now(),
    retries: operation.retries || 0
  };
  
  // 로컬 스토리지에서 기존 큐 가져오기
  const queueString = localStorage.getItem(SYNC_QUEUE_KEY) || '[]';
  let queue: typeof enhancedOperation[] = [];
  
  try {
    queue = JSON.parse(queueString);
  } catch (error) {
    console.error('[네트워크] 동기화 큐 파싱 오류:', error);
    queue = [];
  }
  
  // 큐에 작업 추가
  queue.push(enhancedOperation);
  
  // 큐 저장
  localStorage.setItem(SYNC_QUEUE_KEY, JSON.stringify(queue));
  
  console.log(`[네트워크] 동기화 큐에 작업 추가: ${operation.type} ${operation.entityType}`);
};

/**
 * 동기화 큐 가져오기
 */
export const getSyncQueue = (): SyncQueueItem[] => {
  try {
    const queueStr = localStorage.getItem(SYNC_QUEUE_KEY);
    return queueStr ? JSON.parse(queueStr) : [];
  } catch (error) {
    console.error('[네트워크] 동기화 큐 조회 실패:', error);
    return [];
  }
};

/**
 * 동기화 큐에서 항목 제거
 */
export const removeFromSyncQueue = (itemId: string): void => {
  try {
    const queue = getSyncQueue();
    const updatedQueue = queue.filter(item => item.id !== itemId);
    localStorage.setItem(SYNC_QUEUE_KEY, JSON.stringify(updatedQueue));
    console.log(`[네트워크] 동기화 큐에서 작업 제거: ${itemId}`);
  } catch (error) {
    console.error('[네트워크] 동기화 큐 항목 제거 실패:', error);
  }
};

/**
 * 동기화 큐 항목 업데이트
 */
export const updateSyncQueueItem = (itemId: string, updates: Partial<SyncQueueItem>): void => {
  try {
    const queue = getSyncQueue();
    const updatedQueue = queue.map(item => 
      item.id === itemId ? { ...item, ...updates } : item
    );
    localStorage.setItem(SYNC_QUEUE_KEY, JSON.stringify(updatedQueue));
  } catch (error) {
    console.error('[네트워크] 동기화 큐 항목 업데이트 실패:', error);
  }
};

/**
 * 보류 중인 동기화 작업 처리
 */
export const processPendingSyncQueue = async (): Promise<void> => {
  if (getNetworkStatus() !== 'online') {
    console.log('[네트워크] 오프라인 상태에서 동기화 큐 처리 불가');
    return;
  }
  
  const queue = getSyncQueue();
  if (queue.length === 0) {
    console.log('[네트워크] 처리할 동기화 작업 없음');
    return;
  }
  
  console.log(`[네트워크] ${queue.length}개의 보류 중인 동기화 작업 처리 시작`);
  
  // 큐의 각 항목을 순차적으로 처리
  for (const item of queue) {
    try {
      // 여기서 실제 동기화 작업 수행 (구현 필요)
      // 예: await processSyncItem(item);
      
      // 성공 시 큐에서 제거
      removeFromSyncQueue(item.id);
      console.log(`[네트워크] 동기화 작업 성공: ${item.id}`);
    } catch (error) {
      console.error(`[네트워크] 동기화 작업 실패: ${item.id}`, error);
      
      // 재시도 횟수 증가
      const newRetryCount = item.retryCount + 1;
      
      if (newRetryCount <= MAX_RETRY_COUNT) {
        // 지수 백오프 알고리즘 적용 (재시도 간격을 점점 늘림)
        const retryDelay = INITIAL_RETRY_DELAY * Math.pow(2, newRetryCount - 1);
        console.log(`[네트워크] ${retryDelay}ms 후 재시도 예정 (${newRetryCount}/${MAX_RETRY_COUNT})`);
        
        // 재시도 횟수 업데이트
        updateSyncQueueItem(item.id, { retryCount: newRetryCount });
      } else {
        // 최대 재시도 횟수 초과 시 실패로 처리하고 큐에서 제거
        console.error(`[네트워크] 최대 재시도 횟수 초과, 작업 실패: ${item.id}`);
        removeFromSyncQueue(item.id);
        
        // 사용자에게 알림 (구현 필요)
        // 예: notifyUser(`동기화 작업 실패: ${item.entityType}`);
      }
    }
  }
};

/**
 * 네트워크 요청 래퍼 함수 (재시도 로직 포함)
 */
export const withRetry = async <T>(
  fn: () => Promise<T>,
  options: {
    maxRetries?: number;
    retryDelay?: number;
    onRetry?: (attempt: number, error: Error | unknown) => void;
    shouldRetry?: (error: Error | unknown) => boolean;
    entityType?: string; // 동기화 작업 유형 (예: '트랜잭션 업로드', '예산 다운로드' 등)
  } = {}
): Promise<T> => {
  const {
    maxRetries = 3,
    retryDelay = 1000,
    onRetry = (attempt, error) => console.log(`[네트워크] 재시도 중 (${attempt}/${maxRetries}):`, error),
    shouldRetry = (error) => true // 기본적으로 모든 오류에 대해 재시도
  } = options;
  
  let lastError: Error | unknown;
  
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
    try {
      // 네트워크 상태 확인
      if (attempt > 0 && getNetworkStatus() !== 'online') {
        throw new Error('오프라인 상태입니다.');
      }
      
      return await fn();
    } catch (error) {
      lastError = error;
      
      // 마지막 시도였거나 재시도하지 않아야 하는 오류인 경우
      if (attempt === maxRetries || !shouldRetry(error)) {
        throw error;
      }
      
      // 재시도 콜백 호출
      onRetry(attempt + 1, error);
      
      // 재시도 전 지연
      await new Promise(resolve => setTimeout(resolve, retryDelay * (attempt + 1)));
    }
  }
  
  // 이 코드는 실행되지 않아야 하지만, TypeScript 컴파일러를 위해 추가
  throw lastError;
};

/**
 * 네트워크 요청 타임아웃 래퍼 함수
 */
export const withTimeout = <T>(
  promise: Promise<T>,
  timeoutMs: number = 10000
): Promise<T> => {
  return new Promise<T>((resolve, reject) => {
    const timeoutId = setTimeout(() => {
      reject(new Error(`요청 시간 초과 (${timeoutMs}ms)`));
    }, timeoutMs);
    
    promise
      .then(result => {
        clearTimeout(timeoutId);
        resolve(result);
      })
      .catch(error => {
        clearTimeout(timeoutId);
        reject(error);
      });
  });
};

/**
 * 네트워크 상태 변경 이벤트 리스너 등록
 */
export const onNetworkStatusChange = (callback: (status: NetworkStatus) => void): () => void => {
  const handler = (event: Event) => {
    const customEvent = event as CustomEvent<NetworkStatus>;
    callback(customEvent.detail);
  };
  
  window.addEventListener('networkStatusChange', handler);
  
  // 구독 해제 함수 반환
  return () => {
    window.removeEventListener('networkStatusChange', handler);
  };
};

/**
 * 재시도 옵션 타입
 */
export type RetryOptions = {
  maxRetries?: number;
  retryDelay?: number;
  onRetry?: (attempt: number, error: Error | unknown) => void;
  shouldRetry?: (error: Error | unknown) => boolean;
  entityType?: string; // 동기화 작업 유형 (예: '트랜잭션 업로드', '예산 다운로드' 등)
};
