'use client';

/**
 * Offline Sync Hook
 * 
 * Features:
 * - Queue deliveries for offline submission
 * - Background sync when online
 * - Conflict resolution
 * - Exponential backoff retry
 * - Pre-cache master data
 */

import { useState, useEffect, useCallback, useRef } from 'react';
import api from '../lib/api-client';

// Types
export interface QueuedDelivery {
  id: string;
  timestamp: number;
  data: {
    plan_period_id: number;
    school_id: number;
    commodity_id: number;
    delivery_date: string;
    actual_quantity: number;
    notes?: string;
    quality_issues?: string[];
    quality_status?: 'NO_ISSUES' | 'ISSUES_REPORTED';
    voice_note_url?: string;
    photo_proof_url?: string;
    voice_transcript?: string;
    ocr_text?: string;
    photo_meta?: any;
    units?: any[];
  };
  status: 'pending' | 'syncing' | 'failed' | 'conflict';
  retryCount: number;
  lastError?: string;
  conflictData?: any;
}

export interface CachedMasterData {
  schools: any[];
  commodities: any[];
  suppliers: any[];
  planPeriods: any[];
  cachedAt: number;
}

export interface SyncConflict {
  queuedDelivery: QueuedDelivery;
  serverData: any;
  resolution?: 'keep_local' | 'keep_server' | 'merge';
}

// Storage keys
const QUEUE_KEY = 'gcx_fshs_offline_queue';
const CACHE_KEY = 'gcx_fshs_master_cache';
const SYNC_STATUS_KEY = 'gcx_fshs_sync_status';

// Constants
const MAX_RETRIES = 5;
const BASE_RETRY_DELAY = 1000; // 1 second
const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours

// Exponential backoff delay calculation
const getRetryDelay = (retryCount: number): number => {
  return Math.min(BASE_RETRY_DELAY * Math.pow(2, retryCount), 30000); // Max 30 seconds
};

// Generate unique ID
const generateId = (): string => {
  return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
};

export function useOfflineSync() {
  const [isOnline, setIsOnline] = useState(true);
  const [queue, setQueue] = useState<QueuedDelivery[]>([]);
  const [isSyncing, setIsSyncing] = useState(false);
  const [conflicts, setConflicts] = useState<SyncConflict[]>([]);
  const [cachedData, setCachedData] = useState<CachedMasterData | null>(null);
  const [lastSyncTime, setLastSyncTime] = useState<number | null>(null);
  const syncInProgress = useRef(false);
  const retryTimeouts = useRef<Map<string, NodeJS.Timeout>>(new Map());

  // Load queue from localStorage
  const loadQueue = useCallback(() => {
    try {
      const stored = localStorage.getItem(QUEUE_KEY);
      if (stored) {
        const parsed = JSON.parse(stored);
        setQueue(Array.isArray(parsed) ? parsed : []);
      }
    } catch (e) {
      console.error('Failed to load offline queue:', e);
    }
  }, []);

  // Save queue to localStorage
  const saveQueue = useCallback((newQueue: QueuedDelivery[]) => {
    try {
      localStorage.setItem(QUEUE_KEY, JSON.stringify(newQueue));
      setQueue(newQueue);
    } catch (e) {
      console.error('Failed to save offline queue:', e);
    }
  }, []);

  // Load cached master data
  const loadCachedData = useCallback(() => {
    try {
      const stored = localStorage.getItem(CACHE_KEY);
      if (stored) {
        const parsed = JSON.parse(stored);
        setCachedData(parsed);
        return parsed;
      }
    } catch (e) {
      console.error('Failed to load cached data:', e);
    }
    return null;
  }, []);

  // Save cached master data
  const saveCachedData = useCallback((data: CachedMasterData) => {
    try {
      localStorage.setItem(CACHE_KEY, JSON.stringify(data));
      setCachedData(data);
    } catch (e) {
      console.error('Failed to save cached data:', e);
    }
  }, []);

  // Refresh master data cache
  const refreshCache = useCallback(async () => {
    if (!isOnline) return;

    try {
      const [schoolsRes, commoditiesRes, suppliersRes, periodsRes] = await Promise.all([
        api.get('/schools').catch(() => ({ data: { data: [] } })),
        api.get('/commodities').catch(() => ({ data: { data: [] } })),
        api.get('/suppliers').catch(() => ({ data: { data: [] } })),
        api.get('/plan-periods').catch(() => ({ data: { data: [] } })),
      ]);

      const cacheData: CachedMasterData = {
        schools: schoolsRes.data?.data || [],
        commodities: commoditiesRes.data?.data || [],
        suppliers: suppliersRes.data?.data || [],
        planPeriods: periodsRes.data?.data || [],
        cachedAt: Date.now(),
      };

      saveCachedData(cacheData);
      return cacheData;
    } catch (e: any) {
      // Background cache refresh — never noisy on transient failures.
      const code = e?.code;
      if (code !== 'ERR_NETWORK' && code !== 'ECONNABORTED' && code !== 'ERR_CANCELED') {
        console.warn('[offline-sync] cache refresh skipped', e?.message || e);
      }
      return null;
    }
  }, [isOnline, saveCachedData]);

  // Add delivery to queue
  const queueDelivery = useCallback((deliveryData: QueuedDelivery['data']): string => {
    const id = generateId();
    const queuedItem: QueuedDelivery = {
      id,
      timestamp: Date.now(),
      data: deliveryData,
      status: 'pending',
      retryCount: 0,
    };

    const newQueue = [...queue, queuedItem];
    saveQueue(newQueue);

    // Try to sync immediately if online
    if (isOnline) {
      syncDelivery(queuedItem);
    }

    return id;
  }, [queue, isOnline, saveQueue]);

  // Sync a single delivery
  const syncDelivery = useCallback(async (item: QueuedDelivery) => {
    if (!isOnline || item.status === 'syncing') return;

    // Update status to syncing
    setQueue(prev => {
      const updated = prev.map(q => 
        q.id === item.id ? { ...q, status: 'syncing' as const } : q
      );
      localStorage.setItem(QUEUE_KEY, JSON.stringify(updated));
      return updated;
    });

    try {
      const response = await api.post('/deliveries', item.data);
      
      // Success - remove from queue
      setQueue(prev => {
        const updated = prev.filter(q => q.id !== item.id);
        localStorage.setItem(QUEUE_KEY, JSON.stringify(updated));
        return updated;
      });

      // Trigger haptic feedback on success
      if (navigator.vibrate) {
        navigator.vibrate(100);
      }

      return { success: true, data: response.data };
    } catch (error: any) {
      const statusCode = error.response?.status;
      const errorMessage = error.response?.data?.error || error.message;

      // Handle conflict (409)
      if (statusCode === 409) {
        setQueue(prev => {
          const updated = prev.map(q =>
            q.id === item.id ? { 
              ...q, 
              status: 'conflict' as const,
              conflictData: error.response?.data 
            } : q
          );
          localStorage.setItem(QUEUE_KEY, JSON.stringify(updated));
          return updated;
        });

        setConflicts(prev => [...prev, {
          queuedDelivery: { ...item, status: 'conflict', conflictData: error.response?.data },
          serverData: error.response?.data?.serverData,
        }]);

        return { success: false, conflict: true };
      }

      // Update retry count and status
      const newRetryCount = item.retryCount + 1;
      const shouldRetry = newRetryCount < MAX_RETRIES && statusCode !== 400 && statusCode !== 422;

      setQueue(prev => {
        const updated = prev.map(q =>
          q.id === item.id ? {
            ...q,
            status: shouldRetry ? 'pending' as const : 'failed' as const,
            retryCount: newRetryCount,
            lastError: errorMessage,
          } : q
        );
        localStorage.setItem(QUEUE_KEY, JSON.stringify(updated));
        return updated;
      });

      // Schedule retry with exponential backoff
      if (shouldRetry) {
        const delay = getRetryDelay(newRetryCount);
        const timeout = setTimeout(() => {
          const currentItem = queue.find(q => q.id === item.id);
          if (currentItem && currentItem.status === 'pending') {
            syncDelivery(currentItem);
          }
        }, delay);
        retryTimeouts.current.set(item.id, timeout);
      }

      // Haptic feedback on error
      if (navigator.vibrate) {
        navigator.vibrate([100, 50, 100]);
      }

      return { success: false, error: errorMessage };
    }
  }, [isOnline, queue]);

  // Sync all pending deliveries
  const syncAll = useCallback(async () => {
    if (syncInProgress.current || !isOnline) return;
    
    syncInProgress.current = true;
    setIsSyncing(true);

    const pendingItems = queue.filter(q => q.status === 'pending');
    
    for (const item of pendingItems) {
      await syncDelivery(item);
      // Small delay between syncs to avoid overwhelming server
      await new Promise(resolve => setTimeout(resolve, 500));
    }

    setIsSyncing(false);
    setLastSyncTime(Date.now());
    syncInProgress.current = false;

    localStorage.setItem(SYNC_STATUS_KEY, JSON.stringify({ lastSync: Date.now() }));
  }, [queue, isOnline, syncDelivery]);

  // Resolve conflict
  const resolveConflict = useCallback(async (
    conflictId: string,
    resolution: 'keep_local' | 'keep_server' | 'merge',
    mergedData?: any
  ) => {
    const conflict = conflicts.find(c => c.queuedDelivery.id === conflictId);
    if (!conflict) return;

    if (resolution === 'keep_server') {
      // Remove from queue
      setQueue(prev => {
        const updated = prev.filter(q => q.id !== conflictId);
        localStorage.setItem(QUEUE_KEY, JSON.stringify(updated));
        return updated;
      });
    } else if (resolution === 'keep_local' || resolution === 'merge') {
      // Update queue item and retry
      const dataToSubmit = resolution === 'merge' ? mergedData : conflict.queuedDelivery.data;
      
      setQueue(prev => {
        const updated = prev.map(q =>
          q.id === conflictId ? {
            ...q,
            data: dataToSubmit,
            status: 'pending' as const,
            retryCount: 0,
            conflictData: undefined,
          } : q
        );
        localStorage.setItem(QUEUE_KEY, JSON.stringify(updated));
        return updated;
      });

      // Try syncing again
      const updatedItem = queue.find(q => q.id === conflictId);
      if (updatedItem) {
        syncDelivery({ ...updatedItem, data: dataToSubmit, status: 'pending', retryCount: 0 });
      }
    }

    // Remove from conflicts list
    setConflicts(prev => prev.filter(c => c.queuedDelivery.id !== conflictId));
  }, [conflicts, queue, syncDelivery]);

  // Remove failed delivery from queue
  const removeFromQueue = useCallback((id: string) => {
    setQueue(prev => {
      const updated = prev.filter(q => q.id !== id);
      localStorage.setItem(QUEUE_KEY, JSON.stringify(updated));
      return updated;
    });
    setConflicts(prev => prev.filter(c => c.queuedDelivery.id !== id));
  }, []);

  // Retry failed delivery
  const retryDelivery = useCallback((id: string) => {
    const item = queue.find(q => q.id === id);
    if (item) {
      setQueue(prev => {
        const updated = prev.map(q =>
          q.id === id ? { ...q, status: 'pending' as const, retryCount: 0 } : q
        );
        localStorage.setItem(QUEUE_KEY, JSON.stringify(updated));
        return updated;
      });
      syncDelivery({ ...item, status: 'pending', retryCount: 0 });
    }
  }, [queue, syncDelivery]);

  // Initialize
  useEffect(() => {
    loadQueue();
    loadCachedData();

    // Check if cache needs refresh
    const cached = loadCachedData();
    if (!cached || Date.now() - cached.cachedAt > CACHE_DURATION) {
      refreshCache();
    }
  }, []);

  // Monitor online status
  useEffect(() => {
    const handleOnline = () => {
      setIsOnline(true);
      // Sync pending items when coming back online
      syncAll();
    };

    const handleOffline = () => {
      setIsOnline(false);
    };

    setIsOnline(navigator.onLine);
    window.addEventListener('online', handleOnline);
    window.addEventListener('offline', handleOffline);

    return () => {
      window.removeEventListener('online', handleOnline);
      window.removeEventListener('offline', handleOffline);
    };
  }, [syncAll]);

  // Register background sync if supported
  useEffect(() => {
    if ('serviceWorker' in navigator && 'SyncManager' in window) {
      navigator.serviceWorker.ready.then(registration => {
        // @ts-ignore - SyncManager types
        registration.sync?.register('sync-deliveries').catch(console.error);
      });
    }
  }, [queue]);

  // Cleanup timeouts on unmount
  useEffect(() => {
    return () => {
      retryTimeouts.current.forEach(timeout => clearTimeout(timeout));
    };
  }, []);

  const pendingCount = queue.filter(q => q.status === 'pending').length;
  const failedCount = queue.filter(q => q.status === 'failed').length;
  const conflictCount = conflicts.length;

  return {
    // State
    isOnline,
    isSyncing,
    queue,
    conflicts,
    cachedData,
    lastSyncTime,
    pendingCount,
    failedCount,
    conflictCount,
    
    // Actions
    queueDelivery,
    syncAll,
    syncDelivery,
    resolveConflict,
    removeFromQueue,
    retryDelivery,
    refreshCache,
  };
}
