import { useNavigation } from "@react-navigation/native";
import * as Notifications from "expo-notifications";
import { getItemAsync, setItemAsync } from "expo-secure-store";
import { Platform } from "react-native";
import { useMutation, useQuery, useQueryClient } from "react-query";
import { navigate } from "../../navigation/root-navigator";
import { LOGGING } from "../../utils/logging";
import { SECURESTORE_NOTIFTOKEN, useAuth } from "./auth-provider";
import { useLandmarks } from "./landmarks";
import { usePermissions } from "./PermissionsContext";
import { queryKeys } from "./query-keys";

export interface UserNotification {
    id: string
    user: string
    title: string
    data: string
    read: boolean
}

export type NotifType = "near-landmark" | 'near-landmarks' | "landmark-like"

export const useNotificationToken = () => {
    const { sendApiRequestAsync, userId, accessToken } = useAuth();

    const ensureNotificationTokenExistsOnServer = async (notificationToken: string, retries: number) => {
        LOGGING.log('NOTIFICATIONS', 'info', 'Checking if notification token exists on server, attempt: ' + retries);
        if (retries > 0) {
            try {
                const response = await sendApiRequestAsync({
                    axiosConfig: {
                        method: 'POST',
                        data: {token: notificationToken},
                        url: `/api/notif-token/${userId}/`,
                    },
                    authorized: true,
                    errorMessage: 'Something went wrong when checking notification on server',
                    loggingCategory: 'NOTIFICATIONS',
                });   
                if (response.status === 200) {
                    LOGGING.log('NOTIFICATIONS', 'info', 'Found notification token on server');
                    return response.data;
                }
            } catch (error) {}
    
            await ensureNotificationTokenExistsOnServer(notificationToken, retries - 1)   
        }
        else {
            LOGGING.log('NOTIFICATIONS', 'error', 'Exhausted all error retries when checking notification token on server');
        }
    }
    
    const getNotificationToken = async () => {
        try {
            // first check storage
            let token = await getItemAsync(SECURESTORE_NOTIFTOKEN)
            if (token) {
                const tokenValidationResponse = await ensureNotificationTokenExistsOnServer(token, 3)
                if (tokenValidationResponse) {
                    return token
                }
            }

            // then check server
            const response = await sendApiRequestAsync({
                axiosConfig: {
                    method: 'GET',
                    url: `/api/notif-token/${userId}/`,
                },
                authorized: true,
                errorMessage: 'Something went wrong when retrieving notification token',
                loggingCategory: 'NOTIFICATIONS',
            });   
            
            if (response?.data) {
                LOGGING.log('NOTIFICATIONS', 'info', 'Found notification token on server: ' + response.data);
                return response?.data
            }

            LOGGING.log("NOTIFICATIONS", 'info', 'No token on server, getting one from expo...')
            token = (await Notifications.getExpoPushTokenAsync()).data;      
            if (token) {
                LOGGING.log("NOTIFICATIONS", 'info', 'Got notification token from expo: ' + token);
                await setItemAsync(SECURESTORE_NOTIFTOKEN, token);
            }
            
        } catch (error) {
            console.error(error)
        }
    }

    const {data: token} = useQuery<string, Error>(queryKeys.GET_NOTIFICATION_TOKEN, getNotificationToken, {
        enabled: !!userId && !!accessToken
    })

    return token
}

export const useNotifications = () => {
    const queryClient = useQueryClient();
    const {sendApiRequestAsync, accessToken, userId} = useAuth()

    const getNotifications = async () => {
        const response = await sendApiRequestAsync({
            axiosConfig: { 
                method: 'GET',
                url: `/api/user-profile/notifications/${userId}/`,
            }, 
            authorized: true,
            errorMessage: 'Something went wrong when retrieving notifications',
            loggingCategory: 'NOTIFICATIONS',});   
        return response?.data   
    }

    return useQuery<UserNotification[], Error>(queryKeys.GET_NOTIFICATION_TOKEN, getNotifications, {
        enabled: !!userId&& !!accessToken
    })
}

export const useMarkNotificationRead = () => {
    const queryClient = useQueryClient();
    const {sendApiRequestAsync} = useAuth()

    const markNotificationRead = async (notificationId: string) => {
        const response = await sendApiRequestAsync({
            axiosConfig: {
                method: 'POST',
                url: `/api/user-profile/notifications/mark-read/${notificationId}/`,
            }, 
            authorized: true,
            errorMessage: 'Something went wrong when marking notification as read',
            loggingCategory: 'NOTIFICATIONS',});   
        return response.data;
    }

    return useMutation(markNotificationRead, {
        onSuccess: () => { queryClient.invalidateQueries(queryKeys.GET_NOTIFICATION_TOKEN)},  
        onError: () => queryClient.invalidateQueries(queryKeys.GET_NOTIFICATION_TOKEN),  
    })
}

export const useDeleteNotification = () => {
    const queryClient = useQueryClient();
    const {sendApiRequestAsync} = useAuth()

    const removeNotificaiton = async (notificationId: string) => {
        if (notificationId) {        
            const response = await sendApiRequestAsync({
                axiosConfig: {
                    method: 'DELETE',
                    url: `/api/user-profile/notifications/delete/${notificationId}/`
                },
                authorized: true,
                errorMessage: 'Something went wrong when removing notification',
                loggingCategory: 'NOTIFICATIONS'
            });
            return response.data;
        }
    }

    return useMutation(removeNotificaiton, {
        onSuccess: () => { queryClient.invalidateQueries(queryKeys.GET_NOTIFICATION_TOKEN)},  
        onError: () => queryClient.invalidateQueries(queryKeys.GET_NOTIFICATION_TOKEN),  
    })
}

export const useRegisterNotifications = () => {
    const {notificationPermissionsGranted} = usePermissions();
    const token = useNotificationToken()
    const {refetch: refetchNotifications} = useNotifications()
    const landmarksQuery = useLandmarks()
    const {mutateAsync: markNotificationRead} = useMarkNotificationRead()
    const navigation = useNavigation()
    
    const registerNotificationsAsync = async () => {
        if (notificationPermissionsGranted) {
            if (Platform.OS === 'android') {
                Notifications.setNotificationChannelAsync('default', {
                    name: 'default',
                    importance: Notifications.AndroidImportance.MAX,
                    vibrationPattern: [0, 250, 250, 250],
                    lightColor: '#FF231F7C',
                });
            }   
            return token
        }
        else {
            console.log('[Notifications]: Notification permissions not granted')
            return null
        }
    };

    const handleNotificationInteraction = async (notifData: any) => {
        await markNotificationRead(notifData.notif_id)
        await refetchNotifications()
        console.log(notifData)
        if (notifData?.notif_type as NotifType == 'landmark-like' || notifData?.notif_type as NotifType == 'near-landmark') {
            navigate('Map' as never, {screen: 'OutdoorMap', selectedLandmark: notifData.landmark_id} as never)
        }
        if (notifData?.notif_type as NotifType == 'near-landmarks') {
            navigate('Map' as never, {screen: 'OutdoorMap', selectedLandmark: notifData.landmarks} as never)
        }
    }

    const subscribeToNotifications = async () => {
        const token = await registerNotificationsAsync()
        if (token) {
            const notifReceivedSubscription = Notifications.addNotificationReceivedListener(async notification => { 
                await refetchNotifications()
            })
    
            const notifResponseReceivedSubscription = Notifications.addNotificationResponseReceivedListener(async response => {
                const notifData = response.notification.request.content.data
                handleNotificationInteraction(notifData)
            });

            console.log('[Notifications]: Successfully subcribed to notifications.')
    
            return () => {
                notifReceivedSubscription.remove()
                notifResponseReceivedSubscription.remove()
            }; 
        }
    }

    return {subscribeToNotifications, handleNotificationInteraction}
}
