import axios, { AxiosRequestConfig } from "axios";
import { QueryClient, useMutation, useQuery, useQueryClient } from "react-query";
import { authStore } from "../stores/AuthStore";
import { API_URL, reportAxiosError } from "../utils/RequestUtils";
import { useAuth } from "./useAuth";

/**
 * Interface representing a landmark's comment.
 */
export interface LMComment {
    /**
     * The comment's id.
     */
    id: string,
    /**
     * The id of the user who posted the comment
     */
    poster: string,
    /**
     * The username of the user who posted the comment
     */
     poster_name: string,
    /**
     * The text content of the comment
     */
    content: string
    /**
     * The landmark id that this comment is associated with.
     */
    landmark: string
    /**
     * Timestamp of when the comment was posted.
     */
     timestamp?: Date
    /**
     * Whether or not the comment has been edited.
     */
     edited: boolean
}

/**
 * A custom hook containing [react-query]{@link https://react-query.tanstack.com/} queries and mutations and other logic related to interacting with {@link comment} objects.
 * @category Hooks
 * @namespace useComments
 * @param landmarkId - The id of the current landmark focused by the user. Used to fetch only the comments relevant to that landmark.
 */
export const useComments = (landmarkId: string) => {
    const { refreshAccessToken } = useAuth();

    /**
     * The local instance of the [react-query QueryClient]{@link https://react-query.tanstack.com/reference/QueryClient#_top}.
     * @memberOf useComments
     */
    let queryClient: QueryClient;

    try {
        queryClient = useQueryClient()
    } catch (error) {
        console.log("Something went wrong when retrieving query client: " + error)
    }

    /**
     * The callback responsible for retrieving [comments]{@link LMComment} from the API, used by the [react-query useQuery]{@link https://react-query.tanstack.com/reference/useQuery#_top} hook.
     * * @memberOf useComments
     */
    const getCommentsForLandmark = async (landmarkId: string) => {
        if (landmarkId) {
            const config: AxiosRequestConfig = {
                method: 'GET',
                url: `${API_URL}/api/comments/${landmarkId}`,
                headers: { "Authorization": "Bearer " + authStore.accessToken, }
            } 
    
            try {
                const response = await axios(config);
                return response.data.reverse();
            } catch (error) {
                if (error.response.status == 401) {
                    try {
                        await refreshAccessToken()    
                        const response = await axios({...config, headers: { "Authorization": "Bearer " + authStore.accessToken }});   
                        return response.data.reverse();
                    } catch (error) {
                        // refreshAccessToken will report errors
                    }
                }
    
                reportAxiosError('Something went wrong when retrieving landmarks', error)
                throw new Error;
            }      
        }
    }

     /**
     * The callback responsible for adding a new {@link Landmark} to the server, used by the [react-query useMutation]{@link https://react-query.tanstack.com/reference/useMutation#_top} hook.
     * * @memberOf useLandmarks
     */
    const createComment = async (commentValue: LMComment): Promise<LMComment | undefined> => {
        if (commentValue) {
            const config: AxiosRequestConfig = {
                method: 'POST',
                data: commentValue,
                url: API_URL + `/api/comments/`,
                headers: { "Authorization": "Bearer " + authStore.accessToken, }
            } 
    
            try {
                const response = await axios(config);   
                return response.data;
            } catch (error) {
                if (error.response.status == 401) {
                    try {
                        await refreshAccessToken()    
                        const response = await axios({...config, headers: { "Authorization": "Bearer " + authStore.accessToken }});   
    
                        return response.data;
                    } catch (error) {
                        // refreshAccessToken will report errors
                    }
                }
    
                reportAxiosError('Something went wrong when retrieving landmarks', error)
                throw new Error;
            }   
        }
    }

    /**
     * The callback responsible for updating a {@link Landmark} on the server, used by the [react-query useMutation]{@link https://react-query.tanstack.com/reference/useMutation#_top} hook.
     * * @memberOf useComments
     */
    const editComment =  async (commentValue: LMComment) => {
        if (commentValue) {
            const config: AxiosRequestConfig = {
                method: 'PUT',
                data: {...commentValue, edited: true},
                url: API_URL + `/api/comments/`,
                headers: { "Authorization": "Bearer " + authStore.accessToken, }
            } 
    
            try {
                const response = await axios(config);   
                return response.data;
            } catch (error) {
                if (error.response.status == 401) {
                    try {
                        await refreshAccessToken()    
                        const response = await axios({...config, headers: { "Authorization": "Bearer " + authStore.accessToken }});   
    
                        return response.data;
                    } catch (error) {
                        // refreshAccessToken will report errors
                    }
                }
    
                reportAxiosError('Something went wrong when retrieving landmarks', error)
                throw new Error;
            }   
        }
    }

    /**
     * The callback responsible for deleting a {@link Landmark} from the server, used by the [react-query useMutation]{@link https://react-query.tanstack.com/reference/useMutation#_top} hook.
     * * @memberOf useLandmarks
     */
    const removeComment =  async (id?: string | null) => {
        if (id) {
            const config: AxiosRequestConfig = {
                method: 'DELETE',
                url: API_URL + `/api/comments/${id}`,
                headers: { "Authorization": "Bearer " + authStore.accessToken, }
            } 
            try {
                const response = await axios(config);   
                return response.data;
            } catch (error) {
                if (error.response.status == 401) {
                    try {
                        await refreshAccessToken()    
    
                        // add new access token to header
                        const response = await axios({...config, headers: {"Authorization": "Bearer " + authStore.accessToken}});  
                        return response.data;
                    } catch (error) {
                        // refreshAccessToken will report errors
                    }
                }
    
                reportAxiosError('Something went wrong when retrieving landmarks', error)
                throw new Error;
            }   
        }
    }

    // get-all query
    const { data: comments, status: getCommentsForLandmarkStatus, refetch: refetechCommentsForLandmark } = useQuery<LMComment[], Error>(['getCommentsForLandmark', landmarkId], async () => getCommentsForLandmark(landmarkId), {
        placeholderData: () => queryClient.getQueryData('getCommentsForLandmark'),
        staleTime: 1000,
        refetchInterval: 30000,
        refetchOnReconnect: true,
        refetchOnMount: false
    })

    // mutations
    const { status: addCommentStatus, mutateAsync: addCommentAsync, reset: resetAddComment, data: newComment } = useMutation(createComment, {
        onSuccess: data => {
            queryClient.invalidateQueries('getCommentsForLandmark')
        },  
    })

    const { status: updateCommentStatus, mutateAsync: updateCommentAsync, reset: resetUpdateComment } = useMutation(editComment, {
        onSuccess: () => queryClient.invalidateQueries('getCommentsForLandmark'),  
        onError: () => queryClient.invalidateQueries('getCommentsForLandmark'),  
    })

    const { status: deleteCommentStatus, mutateAsync: deleteCommentAsync, reset: resetDeleteComment } = useMutation(removeComment, {
        onSuccess: () => queryClient.invalidateQueries('getCommentsForLandmark'),  
        onError: () => queryClient.invalidateQueries('getCommentsForLandmark'),  
    })

    return { 
        comments, getCommentsForLandmarkStatus, refetechCommentsForLandmark,  //reading
        addCommentAsync, resetAddComment, addCommentStatus, newComment, // creating
        updateCommentAsync, resetUpdateComment, updateCommentStatus, // updating
        deleteCommentAsync, resetDeleteComment, deleteCommentStatus, // deleting
    }
}
