import { storage } from './storage';
import { api } from './api';
import NetInfo from '@react-native-community/netinfo';

interface QueuedMutation {
  id: string;
  type: 'CREATE' | 'UPDATE' | 'DELETE' | 'COMPLETE';
  endpoint: string;
  data?: any;
  timestamp: number;
  retryCount: number;
}

const QUEUE_KEY = 'offline_mutation_queue';
const OFFLINE_DATA_KEY = 'offline_data';
const MAX_RETRIES = 3;

class OfflineSyncManager {
  private syncInProgress = false;
  private listeners: ((isOnline: boolean) => void)[] = [];
  private isOnline = true;

  constructor() {
    this.initializeNetworkListener();
  }

  private initializeNetworkListener() {
    NetInfo.addEventListener((state) => {
      const wasOffline = !this.isOnline;
      this.isOnline = state.isConnected ?? false;
      
      this.notifyListeners(this.isOnline);
      
      // Auto-sync when coming back online
      if (wasOffline && this.isOnline) {
        console.log('Network restored, starting sync...');
        this.syncAll();
      }
    });

    // Initial check
    NetInfo.fetch().then((state) => {
      this.isOnline = state.isConnected ?? false;
      this.notifyListeners(this.isOnline);
    });
  }

  onNetworkChange(callback: (isOnline: boolean) => void) {
    this.listeners.push(callback);
    return () => {
      this.listeners = this.listeners.filter((cb) => cb !== callback);
    };
  }

  private notifyListeners(isOnline: boolean) {
    this.listeners.forEach((callback) => callback(isOnline));
  }

  getNetworkStatus(): boolean {
    return this.isOnline;
  }

  // Queue a mutation for offline execution
  queueMutation(mutation: Omit<QueuedMutation, 'id' | 'timestamp' | 'retryCount'>) {
    const queue = this.getQueue();
    const newMutation: QueuedMutation = {
      ...mutation,
      id: `${Date.now()}_${Math.random()}`,
      timestamp: Date.now(),
      retryCount: 0,
    };
    
    queue.push(newMutation);
    storage.set(QUEUE_KEY, JSON.stringify(queue));
    
    console.log('Mutation queued:', newMutation.type, newMutation.endpoint);
    
    // Try to sync immediately if online
    if (this.isOnline) {
      this.syncAll();
    }
  }

  private getQueue(): QueuedMutation[] {
    const queueStr = storage.getString(QUEUE_KEY);
    return queueStr ? JSON.parse(queueStr) : [];
  }

  private saveQueue(queue: QueuedMutation[]) {
    storage.set(QUEUE_KEY, JSON.stringify(queue));
  }

  // Execute a single queued mutation
  private async executeMutation(mutation: QueuedMutation): Promise<boolean> {
    try {
      console.log('Executing mutation:', mutation.type, mutation.endpoint);
      
      switch (mutation.type) {
        case 'CREATE':
          await api.post(mutation.endpoint, mutation.data);
          break;
        case 'UPDATE':
          await api.put(mutation.endpoint, mutation.data);
          break;
        case 'DELETE':
          await api.delete(mutation.endpoint);
          break;
        case 'COMPLETE':
          await api.patch(mutation.endpoint);
          break;
      }
      
      console.log('Mutation executed successfully');
      return true;
    } catch (error: any) {
      console.error('Mutation failed:', error.message);
      
      // Don't retry on 404 or 401 errors
      if (error.response?.status === 404 || error.response?.status === 401) {
        console.log('Skipping mutation due to error status');
        return true; // Remove from queue
      }
      
      return false;
    }
  }

  // Sync all queued mutations
  async syncAll(): Promise<void> {
    if (this.syncInProgress) {
      console.log('Sync already in progress, skipping');
      return;
    }

    if (!this.isOnline) {
      console.log('Cannot sync: offline');
      return;
    }

    this.syncInProgress = true;
    console.log('Starting sync...');

    try {
      const queue = this.getQueue();
      console.log(`Found ${queue.length} queued mutations`);

      if (queue.length === 0) {
        return;
      }

      const remainingQueue: QueuedMutation[] = [];

      for (const mutation of queue) {
        const success = await this.executeMutation(mutation);

        if (!success) {
          // Increment retry count
          mutation.retryCount++;
          
          // Keep in queue if under max retries
          if (mutation.retryCount < MAX_RETRIES) {
            remainingQueue.push(mutation);
          } else {
            console.log('Max retries reached for mutation, discarding');
          }
        }

        // Small delay between mutations
        await new Promise((resolve) => setTimeout(resolve, 100));
      }

      this.saveQueue(remainingQueue);
      console.log(`Sync complete. ${remainingQueue.length} mutations remaining`);
    } catch (error) {
      console.error('Sync error:', error);
    } finally {
      this.syncInProgress = false;
    }
  }

  // Save data for offline access
  saveOfflineData(key: string, data: any) {
    const offlineData = this.getOfflineData();
    offlineData[key] = {
      data,
      timestamp: Date.now(),
    };
    storage.set(OFFLINE_DATA_KEY, JSON.stringify(offlineData));
  }

  // Get offline data
  getOfflineData(): any {
    const dataStr = storage.getString(OFFLINE_DATA_KEY);
    return dataStr ? JSON.parse(dataStr) : {};
  }

  // Get specific offline data by key
  getOfflineDataByKey(key: string): any {
    const allData = this.getOfflineData();
    return allData[key]?.data || null;
  }

  // Clear old offline data (older than 7 days)
  clearOldOfflineData() {
    const offlineData = this.getOfflineData();
    const sevenDaysAgo = Date.now() - 7 * 24 * 60 * 60 * 1000;
    
    const cleanedData: any = {};
    Object.keys(offlineData).forEach((key) => {
      if (offlineData[key].timestamp > sevenDaysAgo) {
        cleanedData[key] = offlineData[key];
      }
    });
    
    storage.set(OFFLINE_DATA_KEY, JSON.stringify(cleanedData));
  }

  // Get pending mutation count
  getPendingCount(): number {
    return this.getQueue().length;
  }

  // Clear all queued mutations (for testing/reset)
  clearQueue() {
    storage.delete(QUEUE_KEY);
  }
}

export const offlineSyncManager = new OfflineSyncManager();
