import { ApolloClient, InMemoryCache, HttpLink, gql, DocumentNode, NormalizedCacheObject, OperationVariables } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import createUploadLink from "apollo-upload-client/createUploadLink.mjs";
import { Cookies } from 'react-cookie';

const cookies = new Cookies();
class GraphQLService {
    private client: ApolloClient<NormalizedCacheObject>;
    private client2: ApolloClient<NormalizedCacheObject>;
      
    constructor() {
      const httpLink = new HttpLink({
        uri: process.env.NEXT_PUBLIC_GRAPHQL_API_URL,
      });
  
      const authLink = setContext((_, { headers }) => {
        const apiToken = cookies.get('lr_ap_tk');
        const userToken = cookies.get('lr_usr_tkn');

        return {
          headers: {
            ...headers,
            authorization: apiToken ? `Bearer ${apiToken}` : '',
            statefulauthorization: userToken ? `Bearer ${userToken}` : '',
          },
        };
      });
  
      this.client = new ApolloClient({
        link: authLink.concat(httpLink),
        cache: new InMemoryCache({
            addTypename: false, 
          }),
      });

      this.client2 = new ApolloClient({
        link: createUploadLink({ uri: process.env.NEXT_PUBLIC_GRAPHQL_API_URL }),
        cache: new InMemoryCache(),
      });
    }
  
    async execute<T, V extends OperationVariables = OperationVariables>(query: DocumentNode | string, variables: V = {} as V): Promise<T | null> {
        try {
          const result = await this.client.query<T, V>({
            query: gql`${query}`,
            variables,
            fetchPolicy: 'network-only',
          });
          return result.data;
        } catch (error) {
          //console.error('GraphQL Query Error:', error);
          //return null; 
           let errMessage = "";
          if (error instanceof Error) {
            errMessage = error.message || "";
          }
          if (typeof error === 'object' && error !== null) {
            if ('networkError' in error) {
              const networkError = (error as any).networkError;
              if (networkError && 'result' in networkError && networkError.result) {
                errMessage = networkError.result?.errors[0]?.message || errMessage;
              }
            } else if ('graphQLErrors' in error) {
              const graphQLErrors = (error as { graphQLErrors?: { message: string }[] }).graphQLErrors;
              if (Array.isArray(graphQLErrors) && graphQLErrors.length > 0) {
                errMessage = graphQLErrors[0]?.message || "";
              }
            }
          }
          throw errMessage; 
        }
      }
    
    async mutate<T, V extends OperationVariables = OperationVariables>(mutation: DocumentNode | string, variables: V = {} as V): Promise<T | null |undefined> {
        try {
          const result = await this.client.mutate<T, V>({
            mutation: gql`${mutation}`,
            variables,
          });
          return result.data;
        } catch (error) {
          console.error('GraphQL Mutation Error:', error);
          //return null; 
          let errMessage = "";
          if (error instanceof Error) {
            errMessage = error.message || "";
          }

          if (typeof error === 'object' && error !== null) {
            if ('networkError' in error) {
              const networkError = (error as any).networkError;
              if (networkError && 'result' in networkError && networkError.result) {
                errMessage = networkError.result?.errors[0]?.message || errMessage;
              }
            } else if ('graphQLErrors' in error) {
              const graphQLErrors = (error as { graphQLErrors?: { message: string }[] }).graphQLErrors;
              if (Array.isArray(graphQLErrors) && graphQLErrors.length > 0) {
                errMessage = graphQLErrors[0]?.message || "";
              }
            }
          }

          throw errMessage;
        }
    }

     async mutateFileUpload<T, V extends OperationVariables = OperationVariables>(mutation: DocumentNode | string, variables: V = {} as V): Promise<T | null |undefined> {
      try {
        const apiToken = cookies.get('lr_ap_tk');
        const userToken = cookies.get('lr_usr_tkn');
        const result = await this.client2.mutate<T, V>({
          mutation: gql`${mutation}`,
          variables,
          context: {
              headers: {
                  'apollo-require-preflight': true,
                  authorization: apiToken ? `Bearer ${apiToken}` : '',
                  statefulauthorization: userToken ? `Bearer ${userToken}` : '',
              },
          },
        });

        return result.data;
      } catch (error) {
        console.error('GraphQL Mutation Error:', error);
        //return null; 
        let errMessage = "";
        if (error instanceof Error) {
          errMessage = error.message || "";
        }

        if (typeof error === 'object' && error !== null) {
          if ('networkError' in error) {
            const networkError = (error as any).networkError;
            if (networkError && 'result' in networkError && networkError.result) {
              errMessage = networkError.result?.errors[0]?.message || errMessage;
            }
          } else if ('graphQLErrors' in error) {
            const graphQLErrors = (error as { graphQLErrors?: { message: string }[] }).graphQLErrors;
            if (Array.isArray(graphQLErrors) && graphQLErrors.length > 0) {
              errMessage = graphQLErrors[0]?.message || "";
            }
          }
        }

        throw errMessage;
      }
    } 

  }
  export default GraphQLService;