'use client';

import { useCallback, useEffect, useRef, useState } from 'react';
import api from '../lib/api-client';
import { syncDeliveryEvidence } from '../lib/delivery-evidence-outbox';

export interface QueuedDelivery {
  id: string;
  userId?: number;
  timestamp: number;
  data: {
    plan_period_id: number;
    school_id: number;
    commodity_id: number;
    supplier_id?: number;
    supplier_name?: string;
    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[];
    client_submission_id?: string;
  };
  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';
}

const QUEUE_KEY = 'gcx_fshs_offline_queue';
const CACHE_KEY = 'gcx_fshs_master_cache';
const STATUS_KEY = 'gcx_fshs_sync_status';
const CHANGE_EVENT = 'gcx-delivery-outbox-changed';
const inFlight = new Set<string>();

function readQueue(): QueuedDelivery[] {
  const raw = localStorage.getItem(QUEUE_KEY);
  if (!raw) return [];
  const parsed = JSON.parse(raw);
  if (!Array.isArray(parsed)) throw new Error('The saved delivery queue is invalid.');
  return parsed;
}

function writeQueue(items: QueuedDelivery[]) {
  localStorage.setItem(QUEUE_KEY, JSON.stringify(items));
  window.dispatchEvent(new Event(CHANGE_EVENT));
}

function updateItem(id: string, changes: Partial<QueuedDelivery>) {
  writeQueue(readQueue().map(item => item.id === id ? { ...item, ...changes } : item));
}

function removeItem(id: string) {
  writeQueue(readQueue().filter(item => item.id !== id));
}

export function useOfflineSync() {
  const [isOnline, setIsOnline] = useState(true);
  const [queue, setQueue] = useState<QueuedDelivery[]>([]);
  const [isSyncing, setIsSyncing] = useState(false);
  const [cachedData, setCachedData] = useState<CachedMasterData | null>(null);
  const [lastSyncTime, setLastSyncTime] = useState<number | null>(null);
  const retryTimeouts = useRef(new Map<string, ReturnType<typeof setTimeout>>());
  const syncing = useRef(false);

  const syncDelivery = useCallback(async (item: QueuedDelivery) => {
    if (!navigator.onLine || inFlight.has(item.id)) return;
    inFlight.add(item.id);
    try {
      const me = (await api.get('/auth/me')).data.user;
      if (item.userId && Number(me?.id) !== item.userId) return;
      updateItem(item.id, { status: 'syncing' });
      const response = await api.post('/deliveries', {
        ...item.data,
        client_submission_id: item.data.client_submission_id || item.id,
      });
      removeItem(item.id);
      void syncDeliveryEvidence().catch(() => undefined);
      return { success: true, data: response.data };
    } catch (error: any) {
      const status = error.response?.status;
      const message = error.response?.data?.error || error.message || 'Sync failed';
      if (status === 409) {
        updateItem(item.id, { status: 'conflict', conflictData: error.response?.data, lastError: String(message) });
        return { success: false, conflict: true };
      }
      const attempts = item.retryCount + 1;
      const retryable = !status || status === 401 || status === 429 || status >= 500;
      const shouldRetry = retryable && attempts < 5;
      updateItem(item.id, {
        status: retryable ? 'pending' : 'failed',
        retryCount: attempts,
        lastError: typeof message === 'string' ? message : 'The server rejected this delivery.',
      });
      if (shouldRetry && status !== 401) {
        const timer = setTimeout(() => {
          const current = readQueue().find(record => record.id === item.id);
          if (current?.status === 'pending') void syncDelivery(current);
        }, Math.min(1000 * 2 ** attempts, 30000));
        retryTimeouts.current.set(item.id, timer);
      }
      return { success: false, error: message };
    } finally {
      inFlight.delete(item.id);
    }
  }, []);

  const syncAll = useCallback(async () => {
    if (!navigator.onLine || syncing.current) return;
    syncing.current = true;
    setIsSyncing(true);
    try {
      for (const item of readQueue()) {
        if (item.status === 'pending' || (item.status === 'syncing' && !inFlight.has(item.id))) {
          await syncDelivery(item);
        }
      }
      const timestamp = Date.now();
      setLastSyncTime(timestamp);
      localStorage.setItem(STATUS_KEY, JSON.stringify({ lastSync: timestamp }));
    } finally {
      syncing.current = false;
      setIsSyncing(false);
    }
  }, [syncDelivery]);

  const queueDelivery = useCallback((data: QueuedDelivery['data']): string => {
    const id = data.client_submission_id || (typeof crypto !== 'undefined' && 'randomUUID' in crypto
      ? crypto.randomUUID() : `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`);
    const user = JSON.parse(localStorage.getItem('gcx_fshs_user') || 'null');
    const item: QueuedDelivery = {
      id, userId: user?.id ? Number(user.id) : undefined, timestamp: Date.now(),
      data: { ...data, client_submission_id: id }, status: 'pending', retryCount: 0,
    };
    const existing = readQueue();
    writeQueue([...existing.filter(record => record.id !== id), item]);
    if (navigator.onLine) void syncDelivery(item);
    return id;
  }, [syncDelivery]);

  const refreshCache = useCallback(async () => {
    if (!navigator.onLine) return null;
    try {
      const results = await Promise.all(['/schools', '/commodities', '/suppliers', '/plan-periods'].map(path => api.get(path, { params: { per_page: 1000 } })));
      const cached: CachedMasterData = {
        schools: results[0].data.data || [], commodities: results[1].data.data || [],
        suppliers: results[2].data.data || [], planPeriods: results[3].data.data || [], cachedAt: Date.now(),
      };
      localStorage.setItem(CACHE_KEY, JSON.stringify(cached));
      setCachedData(cached);
      return cached;
    } catch {
      return null;
    }
  }, []);

  const retryDelivery = useCallback((id: string) => {
    updateItem(id, { status: 'pending', retryCount: 0 });
    const item = readQueue().find(record => record.id === id);
    if (item) void syncDelivery(item);
  }, [syncDelivery]);

  const resolveConflict = useCallback(async (id: string, resolution: 'keep_local' | 'keep_server' | 'merge', mergedData?: any) => {
    if (resolution === 'keep_server') {
      removeItem(id);
      return;
    }
    const item = readQueue().find(record => record.id === id);
    if (!item) return;
    updateItem(id, { data: resolution === 'merge' ? mergedData : item.data, conflictData: undefined });
    retryDelivery(id);
  }, [retryDelivery]);

  useEffect(() => {
    const load = () => setQueue(readQueue());
    const online = () => { setIsOnline(true); void syncAll(); };
    const offline = () => setIsOnline(false);
    load();
    setIsOnline(navigator.onLine);
    const cached = localStorage.getItem(CACHE_KEY);
    if (cached) {
      try { setCachedData(JSON.parse(cached)); } catch { /* Old cache will refresh on demand. */ }
    }
    const savedStatus = localStorage.getItem(STATUS_KEY);
    if (savedStatus) {
      try { setLastSyncTime(JSON.parse(savedStatus).lastSync || null); } catch { /* Ignore expired status. */ }
    }
    void syncAll();
    const interval = setInterval(() => { void syncAll(); }, 30000);
    window.addEventListener(CHANGE_EVENT, load);
    window.addEventListener('storage', load);
    window.addEventListener('online', online);
    window.addEventListener('offline', offline);
    return () => {
      clearInterval(interval);
      retryTimeouts.current.forEach(clearTimeout);
      window.removeEventListener(CHANGE_EVENT, load);
      window.removeEventListener('storage', load);
      window.removeEventListener('online', online);
      window.removeEventListener('offline', offline);
    };
  }, [syncAll]);

  const conflicts: SyncConflict[] = queue.filter(item => item.status === 'conflict')
    .map(item => ({ queuedDelivery: item, serverData: item.conflictData?.serverData }));

  return {
    isOnline, isSyncing, queue, conflicts, cachedData, lastSyncTime,
    pendingCount: queue.filter(item => item.status === 'pending').length,
    failedCount: queue.filter(item => item.status === 'failed').length,
    conflictCount: conflicts.length,
    queueDelivery, syncAll, syncDelivery, resolveConflict,
    removeFromQueue: removeItem, retryDelivery, refreshCache,
  };
}
