import { createContext, useContext, useState, useEffect, ReactNode } from 'react';
import * as dataSync from '../api/dataSync';

// UUID generator that works in non-secure contexts (HTTP)
function generateUUID(): string {
  if (typeof crypto !== 'undefined' && crypto.randomUUID) {
    return crypto.randomUUID();
  }
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
    const r = (Math.random() * 16) | 0;
    const v = c === 'x' ? r : (r & 0x3) | 0x8;
    return v.toString(16);
  });
}

export interface SavedLocation {
  id: string;
  name: string;
  latitude: number;
  longitude: number;
}

export interface LocationState {
  latitude: number;
  longitude: number;
  name?: string;
}

interface LocationContextType {
  location: LocationState | null;
  savedLocations: SavedLocation[];
  setLocation: (location: LocationState | null) => void;
  saveLocation: (name: string, lat: number, lng: number) => void;
  deleteLocation: (id: string) => void;
  clearLocation: () => void;
}

const LocationContext = createContext<LocationContextType | null>(null);

const STORAGE_KEY = 'feeld_locations';
const CURRENT_LOCATION_KEY = 'feeld_current_location';

export function LocationProvider({ children }: { children: ReactNode }) {
  const [location, setLocationState] = useState<LocationState | null>(() => {
    const saved = localStorage.getItem(CURRENT_LOCATION_KEY);
    return saved ? JSON.parse(saved) : null;
  });

  const [savedLocations, setSavedLocations] = useState<SavedLocation[]>(() => {
    const saved = localStorage.getItem(STORAGE_KEY);
    return saved ? JSON.parse(saved) : [];
  });

  const [hasSyncedFromServer, setHasSyncedFromServer] = useState(false);

  // Fetch from server on mount and merge with localStorage
  useEffect(() => {
    const syncFromServer = async () => {
      try {
        const serverData = await dataSync.getAllFromServer();
        if (serverData) {
          // Merge saved locations from server
          if (serverData.savedLocations && serverData.savedLocations.length > 0) {
            setSavedLocations(prev => {
              const merged = [...serverData.savedLocations];
              for (const local of prev) {
                if (!merged.some((s: SavedLocation) => s.id === local.id)) {
                  merged.push(local);
                }
              }
              // Update localStorage with merged data
              localStorage.setItem(STORAGE_KEY, JSON.stringify(merged));
              return merged;
            });
          }

          // Restore current location if we don't have one
          if (serverData.currentLocation && !location) {
            setLocationState(serverData.currentLocation);
            localStorage.setItem(CURRENT_LOCATION_KEY, JSON.stringify(serverData.currentLocation));
          }

          // Restore custom location if we don't have one
          if (serverData.customLocation && !location) {
            setLocationState(serverData.customLocation);
            localStorage.setItem(CURRENT_LOCATION_KEY, JSON.stringify(serverData.customLocation));
          }
        }
      } catch (e) {
        console.error('Failed to sync locations from server:', e);
      } finally {
        setHasSyncedFromServer(true);
      }
    };

    syncFromServer();
  }, []);

  useEffect(() => {
    if (location) {
      localStorage.setItem(CURRENT_LOCATION_KEY, JSON.stringify(location));
      dataSync.setData('currentLocation', location);
    } else {
      localStorage.removeItem(CURRENT_LOCATION_KEY);
    }
  }, [location]);

  // Only sync to server after initial load from server is complete
  useEffect(() => {
    if (!hasSyncedFromServer) return;
    localStorage.setItem(STORAGE_KEY, JSON.stringify(savedLocations));
    // Sync to server
    dataSync.setData('savedLocations', savedLocations);
    // Also sync to dedicated saved-locations endpoint for rotation cron
    fetch('/api/saved-locations', {
      method: 'PUT',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ locations: savedLocations }),
    }).catch(() => {}); // Best-effort
  }, [savedLocations, hasSyncedFromServer]);

  const setLocation = (loc: LocationState | null) => {
    setLocationState(loc);
    // Sync to server
    if (loc) {
      dataSync.setData('customLocation', loc);
    }
  };

  const saveLocation = (name: string, latitude: number, longitude: number) => {
    const newLocation: SavedLocation = {
      id: generateUUID(),
      name,
      latitude,
      longitude,
    };
    setSavedLocations((prev) => [...prev, newLocation]);
  };

  const deleteLocation = (id: string) => {
    setSavedLocations((prev) => prev.filter((loc) => loc.id !== id));
  };

  const clearLocation = () => {
    setLocationState(null);
  };

  return (
    <LocationContext.Provider
      value={{
        location,
        savedLocations,
        setLocation,
        saveLocation,
        deleteLocation,
        clearLocation,
      }}
    >
      {children}
    </LocationContext.Provider>
  );
}

export function useLocation() {
  const context = useContext(LocationContext);
  if (!context) {
    throw new Error('useLocation must be used within a LocationProvider');
  }
  return context;
}

// Geocoding helper using OpenStreetMap Nominatim (free, no API key needed)
export async function geocodeAddress(address: string): Promise<{ lat: number; lng: number; displayName: string } | null> {
  try {
    const response = await fetch(
      `https://nominatim.openstreetmap.org/search?format=json&q=${encodeURIComponent(address)}&limit=1`,
      {
        headers: {
          'User-Agent': 'FeeldWebApp/1.0',
        },
      }
    );

    const data = await response.json();

    if (data.length > 0) {
      return {
        lat: parseFloat(data[0].lat),
        lng: parseFloat(data[0].lon),
        displayName: data[0].display_name,
      };
    }

    return null;
  } catch (error) {
    console.error('Geocoding error:', error);
    return null;
  }
}
