/**
 * Fogbreak Offline Service
 * Queue actions while offline, sync when connectivity returns.
 * Caches critical data: client list, today's showings, active deals.
 */

import AsyncStorage from '@react-native-async-storage/async-storage';
import NetInfo, { NetInfoState } from '@react-native-community/netinfo';

const QUEUE_KEY = 'fogbreak_offline_queue';
const CACHE_PREFIX = 'fogbreak_cache_';
const CACHE_EXPIRY_MS = 30 * 60 * 1000; // 30 minutes

export interface OfflineAction {
  id?: string;
  type: string;
  endpoint: string;
  method: string;
  body: Record<string, unknown>;
  timestamp: number;
}

interface CacheEntry<T = unknown> {
  data: T;
  timestamp: number;
  expiresAt: number;
}

// ── Network Status ──

let _isOnline = true;
let _listeners: ((online: boolean) => void)[] = [];

export function initNetworkMonitor(): () => void {
  const unsubscribe = NetInfo.addEventListener((state: NetInfoState) => {
    const online = state.isConnected === true && state.isInternetReachable !== false;
    if (online !== _isOnline) {
      _isOnline = online;
      _listeners.forEach((cb) => cb(online));
      if (online) {
        processQueue();
      }
    }
  });
  return unsubscribe;
}

export async function isOnline(): Promise<boolean> {
  const state = await NetInfo.fetch();
  _isOnline = state.isConnected === true && state.isInternetReachable !== false;
  return _isOnline;
}

export function onConnectivityChange(callback: (online: boolean) => void): () => void {
  _listeners.push(callback);
  return () => {
    _listeners = _listeners.filter((cb) => cb !== callback);
  };
}

// ── Offline Action Queue ──

export async function queueOfflineAction(action: OfflineAction): Promise<void> {
  action.id = `${action.type}_${action.timestamp}_${Math.random().toString(36).slice(2, 8)}`;
  const queue = await getQueue();
  queue.push(action);
  await AsyncStorage.setItem(QUEUE_KEY, JSON.stringify(queue));
}

export async function getQueue(): Promise<OfflineAction[]> {
  try {
    const raw = await AsyncStorage.getItem(QUEUE_KEY);
    return raw ? JSON.parse(raw) : [];
  } catch {
    return [];
  }
}

export async function getQueueLength(): Promise<number> {
  const queue = await getQueue();
  return queue.length;
}

export async function clearQueue(): Promise<void> {
  await AsyncStorage.removeItem(QUEUE_KEY);
}

async function removeFromQueue(actionId: string): Promise<void> {
  const queue = await getQueue();
  const filtered = queue.filter((a) => a.id !== actionId);
  await AsyncStorage.setItem(QUEUE_KEY, JSON.stringify(filtered));
}

export async function processQueue(): Promise<{ processed: number; failed: number }> {
  const queue = await getQueue();
  if (queue.length === 0) return { processed: 0, failed: 0 };

  let processed = 0;
  let failed = 0;

  // Process in order (FIFO)
  for (const action of queue) {
    try {
      // Dynamic import to avoid circular dependency
      const { getToken } = await import('./auth');
      const { getAPIConfig } = await import('./api');

      const token = await getToken();
      const config = getAPIConfig();
      const url = new URL(action.endpoint, config.baseURL);

      if (config.tenantId) {
        url.searchParams.set('tenant_id', config.tenantId);
      }

      const headers: Record<string, string> = {
        'Content-Type': 'application/json',
      };
      if (token) {
        headers['Authorization'] = `Bearer ${token}`;
      }

      const response = await fetch(url.toString(), {
        method: action.method,
        headers,
        body: JSON.stringify(action.body),
      });

      if (response.ok) {
        await removeFromQueue(action.id!);
        processed++;
      } else {
        failed++;
      }
    } catch {
      failed++;
    }
  }

  return { processed, failed };
}

// ── Data Cache ──

export async function cacheData<T>(key: string, data: T, expiryMs?: number): Promise<void> {
  const entry: CacheEntry<T> = {
    data,
    timestamp: Date.now(),
    expiresAt: Date.now() + (expiryMs ?? CACHE_EXPIRY_MS),
  };
  await AsyncStorage.setItem(CACHE_PREFIX + key, JSON.stringify(entry));
}

export async function getCachedData<T>(key: string): Promise<T | null> {
  try {
    const raw = await AsyncStorage.getItem(CACHE_PREFIX + key);
    if (!raw) return null;

    const entry: CacheEntry<T> = JSON.parse(raw);
    if (Date.now() > entry.expiresAt) {
      await AsyncStorage.removeItem(CACHE_PREFIX + key);
      return null;
    }

    return entry.data;
  } catch {
    return null;
  }
}

export async function invalidateCache(key: string): Promise<void> {
  await AsyncStorage.removeItem(CACHE_PREFIX + key);
}

export async function clearAllCache(): Promise<void> {
  const keys = await AsyncStorage.getAllKeys();
  const cacheKeys = keys.filter((k) => k.startsWith(CACHE_PREFIX));
  if (cacheKeys.length > 0) {
    await AsyncStorage.multiRemove(cacheKeys);
  }
}

// Pre-defined cache keys for critical offline data
export const CacheKeys = {
  CLIENTS: 'clients_list',
  TODAY_SHOWINGS: 'today_showings',
  ACTIVE_DEALS: 'active_deals',
  MY_PROFILE: 'my_profile',
  KPI_DATA: 'kpi_data',
  TENANT_CONFIG: 'tenant_config',
} as const;

export async function preCacheEssentials(): Promise<void> {
  try {
    const api = await import('./api');

    const [clients, showings, deals, kpis, tenantConfig] = await Promise.allSettled([
      api.getClients(),
      api.getTodayShowings(),
      api.getDeals({ status: 'active' }),
      api.getKPIs(),
      api.getTenantConfig(),
    ]);

    if (clients.status === 'fulfilled' && clients.value.data) {
      await cacheData(CacheKeys.CLIENTS, clients.value.data, 60 * 60 * 1000); // 1 hour
    }
    if (showings.status === 'fulfilled' && showings.value.data) {
      await cacheData(CacheKeys.TODAY_SHOWINGS, showings.value.data, 15 * 60 * 1000); // 15 min
    }
    if (deals.status === 'fulfilled' && deals.value.data) {
      await cacheData(CacheKeys.ACTIVE_DEALS, deals.value.data, 30 * 60 * 1000);
    }
    if (kpis.status === 'fulfilled' && kpis.value.data) {
      await cacheData(CacheKeys.KPI_DATA, kpis.value.data, 30 * 60 * 1000);
    }
    if (tenantConfig.status === 'fulfilled' && tenantConfig.value.data) {
      await cacheData(CacheKeys.TENANT_CONFIG, tenantConfig.value.data, 24 * 60 * 60 * 1000); // 24 hours
    }
  } catch {
    // Silent fail — cache is best-effort
  }
}
