// Data sync service - syncs with server, falls back to localStorage

// In Docker, nginx proxies /api to backend. Locally, use localhost:3001
const SERVER_URL = import.meta.env.VITE_API_URL ||
  (window.location.port === '5173' ? 'http://localhost:3001/api' : '/api');
const LOCAL_STORAGE_PREFIX = 'feeld_';

// Use a consistent user ID for single-user mode
// This ensures all data goes to the same file regardless of profile ID changes
const getUserId = (): string => {
  return 'user';
};

// Check if server is available
let serverAvailable: boolean | null = null;
let serverCheckPromise: Promise<boolean> | null = null;

const checkServerAvailable = async (): Promise<boolean> => {
  if (serverAvailable !== null) return serverAvailable;

  if (!serverCheckPromise) {
    serverCheckPromise = fetch(`${SERVER_URL}/health`, { method: 'GET' })
      .then(res => {
        serverAvailable = res.ok;
        return serverAvailable;
      })
      .catch(() => {
        serverAvailable = false;
        return false;
      })
      .finally(() => {
        // Recheck every 30 seconds
        setTimeout(() => {
          serverAvailable = null;
          serverCheckPromise = null;
        }, 30000);
      });
  }

  return serverCheckPromise;
};

// Sync a specific key to the server
export const syncToServer = async (key: string, value: any): Promise<boolean> => {
  const isAvailable = await checkServerAvailable();
  if (!isAvailable) return false;

  try {
    const response = await fetch(`${SERVER_URL}/data/${getUserId()}/${key}`, {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ value }),
    });
    return response.ok;
  } catch (e) {
    console.error('Failed to sync to server:', e);
    return false;
  }
};

// Get data from server
export const getFromServer = async (key: string): Promise<any | null> => {
  const isAvailable = await checkServerAvailable();
  if (!isAvailable) return null;

  try {
    const response = await fetch(`${SERVER_URL}/data/${getUserId()}/${key}`);
    if (response.ok) {
      const data = await response.json();
      return data[key];
    }
    return null;
  } catch (e) {
    console.error('Failed to get from server:', e);
    return null;
  }
};

// Get all data from server
export const getAllFromServer = async (): Promise<any | null> => {
  const isAvailable = await checkServerAvailable();
  if (!isAvailable) return null;

  try {
    const response = await fetch(`${SERVER_URL}/data/${getUserId()}`);
    if (response.ok) {
      return await response.json();
    }
    return null;
  } catch (e) {
    console.error('Failed to get all from server:', e);
    return null;
  }
};

// Combined get: try server first, fall back to localStorage
export const getData = async <T>(key: string, defaultValue: T): Promise<T> => {
  // Try server first
  const serverData = await getFromServer(key);
  if (serverData !== null) {
    // Update localStorage with server data
    localStorage.setItem(`${LOCAL_STORAGE_PREFIX}${key}`, JSON.stringify(serverData));
    return serverData as T;
  }

  // Fall back to localStorage
  const localData = localStorage.getItem(`${LOCAL_STORAGE_PREFIX}${key}`);
  if (localData) {
    try {
      return JSON.parse(localData) as T;
    } catch (e) {
      return defaultValue;
    }
  }

  return defaultValue;
};

// Combined set: save to both localStorage and server
export const setData = async (key: string, value: any): Promise<void> => {
  // Always save to localStorage first (immediate)
  localStorage.setItem(`${LOCAL_STORAGE_PREFIX}${key}`, JSON.stringify(value));

  // Then sync to server (async)
  syncToServer(key, value);
};

// Liked profiles specific functions
export const addLikedProfile = async (id: string, name?: string): Promise<void> => {
  const isAvailable = await checkServerAvailable();

  // Get current liked profiles
  const current = await getData<Array<{ id: string; name?: string; likedAt: number }>>('liked_profiles', []);

  // Don't add duplicates
  if (current.some(p => p.id === id)) return;

  const newProfile = { id, name, likedAt: Date.now() };
  const updated = [newProfile, ...current];

  // Save to localStorage
  localStorage.setItem(`${LOCAL_STORAGE_PREFIX}liked_profiles`, JSON.stringify(updated));

  // Sync to server
  if (isAvailable) {
    try {
      await fetch(`${SERVER_URL}/data/${getUserId()}/liked-profiles`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ id, name }),
      });
    } catch (e) {
      console.error('Failed to sync liked profile to server:', e);
    }
  }
};

export const removeLikedProfile = async (id: string): Promise<void> => {
  const isAvailable = await checkServerAvailable();

  // Get current and filter
  const current = await getData<Array<{ id: string; name?: string; likedAt: number }>>('liked_profiles', []);
  const updated = current.filter(p => p.id !== id);

  // Save to localStorage
  localStorage.setItem(`${LOCAL_STORAGE_PREFIX}liked_profiles`, JSON.stringify(updated));

  // Sync to server
  if (isAvailable) {
    try {
      await fetch(`${SERVER_URL}/data/${getUserId()}/liked-profiles/${encodeURIComponent(id)}`, {
        method: 'DELETE',
      });
    } catch (e) {
      console.error('Failed to remove liked profile from server:', e);
    }
  }
};

export const getLikedProfiles = async (): Promise<Array<{ id: string; name?: string; likedAt: number }>> => {
  return getData('liked_profiles', []);
};

// Initial sync: MERGE data from server and localStorage (keep richer data from both)
export const initialSync = async (): Promise<void> => {
  const serverData = await getAllFromServer();

  // Helper to merge arrays by ID, keeping all unique items
  const mergeArraysById = (serverArr: any[] | undefined, localArr: any[], idField: string = 'id'): any[] => {
    const merged = [...(serverArr || [])];
    for (const local of localArr) {
      if (!merged.some(s => s[idField] === local[idField])) {
        merged.push(local);
      }
    }
    return merged;
  };

  // Merge liked profiles (keep all from both sources)
  const localLiked = JSON.parse(localStorage.getItem(`${LOCAL_STORAGE_PREFIX}liked_profiles`) || '[]');
  const mergedLiked = mergeArraysById(serverData?.likedProfiles, localLiked, 'id');
  localStorage.setItem(`${LOCAL_STORAGE_PREFIX}liked_profiles`, JSON.stringify(mergedLiked));
  // Sync merged result back to server if we added local items
  if (mergedLiked.length > (serverData?.likedProfiles?.length || 0)) {
    await setData('likedProfiles', mergedLiked);
  }

  // Merge saved locations
  const localLocations = JSON.parse(localStorage.getItem(`${LOCAL_STORAGE_PREFIX}saved_locations`) || '[]');
  const mergedLocations = mergeArraysById(serverData?.savedLocations, localLocations, 'name');
  localStorage.setItem(`${LOCAL_STORAGE_PREFIX}saved_locations`, JSON.stringify(mergedLocations));
  localStorage.setItem(`feeld_locations`, JSON.stringify(mergedLocations));
  if (mergedLocations.length > (serverData?.savedLocations?.length || 0)) {
    await setData('savedLocations', mergedLocations);
  }

  // Credentials: prefer server if available (more authoritative)
  if (serverData?.profileId) {
    localStorage.setItem(`${LOCAL_STORAGE_PREFIX}profile_id`, serverData.profileId);
  }
  if (serverData?.refreshToken) {
    localStorage.setItem(`${LOCAL_STORAGE_PREFIX}refresh_token`, serverData.refreshToken);
  }
  if (serverData?.analyticsId) {
    localStorage.setItem(`${LOCAL_STORAGE_PREFIX}analytics_id`, serverData.analyticsId);
  }

  // Current location: prefer server if local is empty
  if (serverData?.currentLocation) {
    const localCurrent = localStorage.getItem('feeld_current_location');
    if (!localCurrent) {
      localStorage.setItem('feeld_current_location', JSON.stringify(serverData.currentLocation));
    }
  }

  // Custom location: prefer server if local is empty
  if (serverData?.customLocation) {
    const localCustom = localStorage.getItem(`${LOCAL_STORAGE_PREFIX}custom_location`);
    if (!localCustom) {
      localStorage.setItem(`${LOCAL_STORAGE_PREFIX}custom_location`, JSON.stringify(serverData.customLocation));
    }
  }

  console.log('Synced and merged data from server', {
    likedProfiles: mergedLiked.length,
    savedLocations: mergedLocations.length,
  });
};

// Save credentials to server - merges with existing server data to prevent data loss
export const saveCredentials = async (profileId: string, refreshToken: string, analyticsId?: string): Promise<void> => {
  const isAvailable = await checkServerAvailable();

  // Save to localStorage
  localStorage.setItem(`${LOCAL_STORAGE_PREFIX}profile_id`, profileId);
  localStorage.setItem(`${LOCAL_STORAGE_PREFIX}refresh_token`, refreshToken);
  if (analyticsId) {
    localStorage.setItem(`${LOCAL_STORAGE_PREFIX}analytics_id`, analyticsId);
  }

  // Sync to server - but first pull server data to merge
  if (isAvailable) {
    try {
      // Get existing server data first
      const serverData = await getAllFromServer();

      // Only update credentials, preserve everything else on server
      await setData('profileId', profileId);
      await setData('refreshToken', refreshToken);
      if (analyticsId) {
        await setData('analyticsId', analyticsId);
      }
      await setData('updatedAt', new Date().toISOString());

      // If server had data that localStorage doesn't, restore it to localStorage
      if (serverData) {
        if (serverData.likedProfiles && serverData.likedProfiles.length > 0) {
          const localLiked = JSON.parse(localStorage.getItem(`${LOCAL_STORAGE_PREFIX}liked_profiles`) || '[]');
          // Merge: keep all from server, add any from local that aren't duplicates
          const mergedLiked = [...serverData.likedProfiles];
          for (const local of localLiked) {
            if (!mergedLiked.some(s => s.id === local.id)) {
              mergedLiked.push(local);
            }
          }
          if (mergedLiked.length > localLiked.length) {
            localStorage.setItem(`${LOCAL_STORAGE_PREFIX}liked_profiles`, JSON.stringify(mergedLiked));
            console.log(`Restored ${mergedLiked.length - localLiked.length} liked profiles from server`);
          }
        }

        if (serverData.savedLocations && serverData.savedLocations.length > 0) {
          const localLocations = JSON.parse(localStorage.getItem(`${LOCAL_STORAGE_PREFIX}saved_locations`) || '[]');
          if (serverData.savedLocations.length > localLocations.length) {
            localStorage.setItem(`${LOCAL_STORAGE_PREFIX}saved_locations`, JSON.stringify(serverData.savedLocations));
            console.log(`Restored ${serverData.savedLocations.length} saved locations from server`);
          }
        }

        if (serverData.currentLocation && !localStorage.getItem('feeld_current_location')) {
          localStorage.setItem('feeld_current_location', JSON.stringify(serverData.currentLocation));
          console.log('Restored current location from server');
        }

        if (serverData.customLocation && !localStorage.getItem(`${LOCAL_STORAGE_PREFIX}custom_location`)) {
          localStorage.setItem(`${LOCAL_STORAGE_PREFIX}custom_location`, JSON.stringify(serverData.customLocation));
          console.log('Restored custom location from server');
        }
      }
    } catch (e) {
      console.error('Failed to sync credentials to server:', e);
    }
  }
};

// Save custom location to server
export const saveCustomLocation = async (location: { latitude: number; longitude: number; name?: string }): Promise<void> => {
  localStorage.setItem(`${LOCAL_STORAGE_PREFIX}custom_location`, JSON.stringify(location));
  await setData('customLocation', location);
};

// Sent pings specific functions - uses dedicated /sent-pings endpoint
export interface SentPing {
  targetProfileId: string;
  targetName?: string;
  message?: string;
  sentAt: number;
  status: 'SENT' | 'MATCHED' | 'EXPIRED';
}

export const addSentPing = async (targetProfileId: string, targetName?: string, message?: string): Promise<void> => {
  const isAvailable = await checkServerAvailable();

  const newPing: SentPing = {
    targetProfileId,
    targetName,
    message,
    sentAt: Date.now(),
    status: 'SENT',
  };

  // Save to localStorage as backup
  const current = JSON.parse(localStorage.getItem(`${LOCAL_STORAGE_PREFIX}sent_pings`) || '[]');
  if (!current.some((p: SentPing) => p.targetProfileId === targetProfileId)) {
    const updated = [newPing, ...current];
    localStorage.setItem(`${LOCAL_STORAGE_PREFIX}sent_pings`, JSON.stringify(updated));
  }

  // Sync to dedicated sent pings endpoint
  if (isAvailable) {
    try {
      await fetch(`${SERVER_URL}/sent-pings`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ targetProfileId, targetName, message }),
      });
    } catch (e) {
      console.error('Failed to sync sent ping to server:', e);
    }
  }
};

export const updateSentPingStatus = async (targetProfileId: string, status: 'SENT' | 'MATCHED' | 'EXPIRED'): Promise<void> => {
  // Update localStorage
  const current = JSON.parse(localStorage.getItem(`${LOCAL_STORAGE_PREFIX}sent_pings`) || '[]');
  const updated = current.map((p: SentPing) =>
    p.targetProfileId === targetProfileId ? { ...p, status } : p
  );
  localStorage.setItem(`${LOCAL_STORAGE_PREFIX}sent_pings`, JSON.stringify(updated));

  // Update server
  const isAvailable = await checkServerAvailable();
  if (isAvailable) {
    try {
      await fetch(`${SERVER_URL}/sent-pings/${encodeURIComponent(targetProfileId)}`, {
        method: 'PUT',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ status }),
      });
    } catch (e) {
      console.error('Failed to update sent ping status on server:', e);
    }
  }
};

export const removeSentPing = async (targetProfileId: string): Promise<void> => {
  // Update localStorage
  const current = JSON.parse(localStorage.getItem(`${LOCAL_STORAGE_PREFIX}sent_pings`) || '[]');
  const updated = current.filter((p: SentPing) => p.targetProfileId !== targetProfileId);
  localStorage.setItem(`${LOCAL_STORAGE_PREFIX}sent_pings`, JSON.stringify(updated));

  // Delete from server
  const isAvailable = await checkServerAvailable();
  if (isAvailable) {
    try {
      await fetch(`${SERVER_URL}/sent-pings/${encodeURIComponent(targetProfileId)}`, {
        method: 'DELETE',
      });
    } catch (e) {
      console.error('Failed to remove sent ping from server:', e);
    }
  }
};

export const getSentPings = async (): Promise<SentPing[]> => {
  const isAvailable = await checkServerAvailable();

  // Try server first
  if (isAvailable) {
    try {
      const response = await fetch(`${SERVER_URL}/sent-pings`);
      if (response.ok) {
        const data = await response.json();
        const pings = data.pings || [];
        // Update localStorage with server data
        localStorage.setItem(`${LOCAL_STORAGE_PREFIX}sent_pings`, JSON.stringify(pings));
        return pings;
      }
    } catch (e) {
      console.error('Failed to fetch sent pings from server:', e);
    }
  }

  // Fallback to localStorage
  return JSON.parse(localStorage.getItem(`${LOCAL_STORAGE_PREFIX}sent_pings`) || '[]');
};

export const clearAllSentPings = async (): Promise<void> => {
  // Clear localStorage
  localStorage.setItem(`${LOCAL_STORAGE_PREFIX}sent_pings`, '[]');

  // Clear server by getting all pings and deleting them
  const isAvailable = await checkServerAvailable();
  if (isAvailable) {
    try {
      const response = await fetch(`${SERVER_URL}/sent-pings`);
      if (response.ok) {
        const data = await response.json();
        const pings = data.pings || [];
        // Delete each ping
        for (const ping of pings) {
          await fetch(`${SERVER_URL}/sent-pings/${encodeURIComponent(ping.targetProfileId)}`, {
            method: 'DELETE',
          });
        }
      }
    } catch (e) {
      console.error('Failed to clear sent pings from server:', e);
    }
  }
};

// Disliked profiles specific functions - uses dedicated /disliked-profiles endpoint
export interface DislikedProfile {
  id: string;
  imaginaryName?: string;
  age?: number;
  gender?: string;
  sexuality?: string;
  photos?: any[];
  dislikedAt: string;
}

export const addDislikedProfile = async (profile: Omit<DislikedProfile, 'dislikedAt'>): Promise<void> => {
  const isAvailable = await checkServerAvailable();

  const newProfile: DislikedProfile = {
    ...profile,
    dislikedAt: new Date().toISOString(),
  };

  // Save to localStorage as backup
  const current = JSON.parse(localStorage.getItem(`${LOCAL_STORAGE_PREFIX}disliked_profiles`) || '[]');
  if (!current.some((p: DislikedProfile) => p.id === profile.id)) {
    const updated = [newProfile, ...current];
    localStorage.setItem(`${LOCAL_STORAGE_PREFIX}disliked_profiles`, JSON.stringify(updated));
  }

  // Sync to dedicated disliked profiles endpoint
  if (isAvailable) {
    try {
      await fetch(`${SERVER_URL}/disliked-profiles`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ profile }),
      });
    } catch (e) {
      console.error('Failed to sync disliked profile to server:', e);
    }
  }
};

export const removeDislikedProfile = async (profileId: string): Promise<void> => {
  // Update localStorage
  const current = JSON.parse(localStorage.getItem(`${LOCAL_STORAGE_PREFIX}disliked_profiles`) || '[]');
  const updated = current.filter((p: DislikedProfile) => p.id !== profileId);
  localStorage.setItem(`${LOCAL_STORAGE_PREFIX}disliked_profiles`, JSON.stringify(updated));

  // Delete from server
  const isAvailable = await checkServerAvailable();
  if (isAvailable) {
    try {
      await fetch(`${SERVER_URL}/disliked-profiles/${encodeURIComponent(profileId)}`, {
        method: 'DELETE',
      });
    } catch (e) {
      console.error('Failed to remove disliked profile from server:', e);
    }
  }
};

export const getDislikedProfiles = async (): Promise<DislikedProfile[]> => {
  const isAvailable = await checkServerAvailable();

  // Try server first
  if (isAvailable) {
    try {
      const response = await fetch(`${SERVER_URL}/disliked-profiles`);
      if (response.ok) {
        const data = await response.json();
        const profiles = data.profiles || [];
        // Update localStorage with server data
        localStorage.setItem(`${LOCAL_STORAGE_PREFIX}disliked_profiles`, JSON.stringify(profiles));
        return profiles;
      }
    } catch (e) {
      console.error('Failed to fetch disliked profiles from server:', e);
    }
  }

  // Fallback to localStorage
  return JSON.parse(localStorage.getItem(`${LOCAL_STORAGE_PREFIX}disliked_profiles`) || '[]');
};

export const clearAllDislikedProfiles = async (): Promise<void> => {
  // Clear localStorage
  localStorage.setItem(`${LOCAL_STORAGE_PREFIX}disliked_profiles`, '[]');

  // Clear server by getting all profiles and deleting them
  const isAvailable = await checkServerAvailable();
  if (isAvailable) {
    try {
      const response = await fetch(`${SERVER_URL}/disliked-profiles`);
      if (response.ok) {
        const data = await response.json();
        const profiles = data.profiles || [];
        // Delete each profile
        for (const profile of profiles) {
          await fetch(`${SERVER_URL}/disliked-profiles/${encodeURIComponent(profile.id)}`, {
            method: 'DELETE',
          });
        }
      }
    } catch (e) {
      console.error('Failed to clear disliked profiles from server:', e);
    }
  }
};

// Favorites - "saved for later" queue, local-only (no remote Feeld action)
export interface Favorite {
  targetProfileId: string;
  savedAt: number;
  profile?: any;
}

export const addFavorite = async (targetProfileId: string, profile?: any): Promise<void> => {
  const newFav: Favorite = { targetProfileId, savedAt: Date.now(), profile };

  const current: Favorite[] = JSON.parse(localStorage.getItem(`${LOCAL_STORAGE_PREFIX}favorites`) || '[]');
  if (!current.some(f => f.targetProfileId === targetProfileId)) {
    localStorage.setItem(`${LOCAL_STORAGE_PREFIX}favorites`, JSON.stringify([newFav, ...current]));
  }

  if (await checkServerAvailable()) {
    try {
      await fetch(`${SERVER_URL}/favorites`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ targetProfileId, profile }),
      });
    } catch (e) {
      console.error('Failed to sync favorite to server:', e);
    }
  }
};

export const removeFavorite = async (targetProfileId: string): Promise<void> => {
  const current: Favorite[] = JSON.parse(localStorage.getItem(`${LOCAL_STORAGE_PREFIX}favorites`) || '[]');
  localStorage.setItem(
    `${LOCAL_STORAGE_PREFIX}favorites`,
    JSON.stringify(current.filter(f => f.targetProfileId !== targetProfileId)),
  );

  if (await checkServerAvailable()) {
    try {
      await fetch(`${SERVER_URL}/favorites/${encodeURIComponent(targetProfileId)}`, { method: 'DELETE' });
    } catch (e) {
      console.error('Failed to remove favorite from server:', e);
    }
  }
};

export const getFavorites = async (): Promise<Favorite[]> => {
  if (await checkServerAvailable()) {
    try {
      const r = await fetch(`${SERVER_URL}/favorites`);
      if (r.ok) {
        const data = await r.json();
        const favs = data.favorites || [];
        localStorage.setItem(`${LOCAL_STORAGE_PREFIX}favorites`, JSON.stringify(favs));
        return favs;
      }
    } catch (e) {
      console.error('Failed to fetch favorites from server:', e);
    }
  }
  return JSON.parse(localStorage.getItem(`${LOCAL_STORAGE_PREFIX}favorites`) || '[]');
};

export const clearAllFavorites = async (): Promise<void> => {
  localStorage.setItem(`${LOCAL_STORAGE_PREFIX}favorites`, '[]');
  if (await checkServerAvailable()) {
    try {
      await fetch(`${SERVER_URL}/favorites`, { method: 'DELETE' });
    } catch (e) {
      console.error('Failed to clear favorites on server:', e);
    }
  }
};

// Export server status check
export const isServerAvailable = checkServerAvailable;
