import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
import { useFonts } from 'expo-font';
import { Stack } from 'expo-router';
import * as SplashScreen from 'expo-splash-screen';
import 'react-native-reanimated';
import { useColorScheme } from '@/hooks/useColorScheme';
import React, { useState, useEffect, useRef } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
import UserSelection from '@/components/UserSelection';
import { TouchableOpacity, Text, View } from 'react-native';
import * as Notifications from 'expo-notifications';
import * as Device from 'expo-device';
import Constants from 'expo-constants';

// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync();

type User = {
  id: number;
  name: string;
  message: string;
};

export default function RootLayout() {
  const API_KEY = process.env.EXPO_PUBLIC_API_KEY;
  const BASE_URL = process.env.EXPO_PUBLIC_BASE_URL;

  const colorScheme = useColorScheme();
  const [loaded, error] = useFonts({
    SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
  });
  const [user, setUser] = useState<User | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  
  // Push Notifications state
  const [expoPushToken, setExpoPushToken] = useState<string | null>(null);
  const notificationListener = useRef<Notifications.Subscription>();
  const responseListener = useRef<Notifications.Subscription>();

  useEffect(() => {
    async function prepare() {
      try {
        // Load the user
        const storedUser = await AsyncStorage.getItem('@user');
        if (storedUser) {
          setUser(JSON.parse(storedUser));
        }
      } catch (e) {
        console.error('Failed to load user', e);
      } finally {
        setIsLoading(false);
      }
    }

    prepare();
  }, []);

  useEffect(() => {
    if (loaded) {
      SplashScreen.hideAsync();
    }
  }, [loaded]);

  const handleUserSelected = async (selectedUser: User) => {
    setUser(selectedUser);
    try {
      await AsyncStorage.setItem('@user', JSON.stringify(selectedUser));
      
      // Store the Push Token to your server when the user is selected (optional)
      if (expoPushToken) {
        await savePushToken(selectedUser.id, expoPushToken); // Hypothetical function to send token to server
      }
    } catch (e) {
      console.error('Failed to save user or push token', e);
    }
  };

  const handleSwitchUser = async () => {
    try {
      await AsyncStorage.removeItem('@user');
      setUser(null);
    } catch (e) {
      console.error('Failed to remove user', e);
    }
  };

  /** --- PUSH NOTIFICATIONS LOGIC --- **/

useEffect(() => {
  // Register for push notifications and set the push token
  registerForPushNotificationsAsync().then(async (token) => {
    if (token) {
      setExpoPushToken(token);

      // Upload push token to backend when successfully received
      try {
        const storedUser = await AsyncStorage.getItem('@user');
        if (storedUser) {
          const user = JSON.parse(storedUser);

          // Send the push token to your Next.js API, along with the user's ID
          await fetch(`${BASE_URL}/api/updatePushToken`, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
            },
            body: JSON.stringify({
              apiKey: API_KEY,  // Use the API key stored in the environment
              userId: user.id,  // The logged-in user's ID
              pushToken: token, // The Expo push token collected
            }),
          });

          console.log('Push token successfully sent to backend');
        } else {
          console.log('No user found in AsyncStorage');
        }
      } catch (error) {
        console.error('Failed to send push token to backend', error);
      }
    }
  });

  // Listener for received notifications while the app is running
  notificationListener.current = Notifications.addNotificationReceivedListener(notification => {
    console.log('Notification Received: ', notification);
  });

  // Listener for when the user interacts with a notification
  responseListener.current = Notifications.addNotificationResponseReceivedListener(response => {
    console.log('User interacted with notification: ', response);
  });

  // Clean up listeners when component unmounts
  return () => {
    if (notificationListener.current) {
      Notifications.removeNotificationSubscription(notificationListener.current);
    }
    if (responseListener.current) {
      Notifications.removeNotificationSubscription(responseListener.current);
    }
  };
}, []);

  if (!loaded || isLoading) {
    return null; // or a more elegant loading indicator
  }

  if (!user) {
    return <UserSelection onUserSelected={handleUserSelected} />;
  }

  return (
    <ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
      <Stack>
        <Stack.Screen 
          name="(tabs)" 
          options={{ 
            headerShown: true,
            title: user.name,
            headerRight: () => (
              <TouchableOpacity onPress={handleSwitchUser} style={{ marginRight: 15 }}>
                <Text style={{ color: colorScheme === 'dark' ? 'white' : 'black' }}>
                  Switch User
                </Text>
              </TouchableOpacity>
            ),
          }} 
        />
        <Stack.Screen name="+not-found" />
      </Stack>

      {/* You can display the push token for debug purposes */}
      {expoPushToken && (
        <View style={{ padding: 10 }}>
          <Text>Your Push Token: {expoPushToken}</Text>
        </View>
      )}
    </ThemeProvider>
  );
}

/** --- Helper functions for push notifications --- **/

// Function to get the push token and request permissions
async function registerForPushNotificationsAsync() {
  let token;

  // Ensure you're running on a physical device
  if (Device.isDevice) {
    const { status: existingStatus } = await Notifications.getPermissionsAsync();
    let finalStatus = existingStatus;

    // Ask user for permission if not granted already
    if (existingStatus !== 'granted') {
      const { status } = await Notifications.requestPermissionsAsync();
      finalStatus = status;
    }

    if (finalStatus !== 'granted') {
      console.log('Failed to get push notification token because permission not granted.');
      return null;
    }

    try {
      token = (await Notifications.getExpoPushTokenAsync()).data;
      console.log('Expo Push Token:', token);
    } catch (error) {
      console.log('Failed to get Expo push token:', error);
      return null;
    }
  } else {
    console.log('Must use physical device for push notifications');
  }

  return token;
}

// (Optional) A function to store push tokens in your backend server
async function savePushToken(userId: number, expoPushToken: string) {
  // You would need to implement this function to save the user's push token to your backend.
  // For example, you could send a POST request to your Next.js API that stores expoPushToken
  console.log('Saving push token for user:', userId, 'with token:', expoPushToken);
}
