// Импорт необходимых модулей
import {
  ApolloClient,
  InMemoryCache,
  ApolloLink,
  HttpLink,
  split,
} from '@apollo/client/core';
import { ApolloClients } from '@vue/apollo-composable';
import { WebSocketLink } from '@apollo/client/link/ws';
import { getMainDefinition } from '@apollo/client/utilities';
import { onError } from '@apollo/client/link/error';
import { boot } from 'quasar/wrappers';
import { LocalStorage } from 'quasar';
import { SessionStorage } from 'quasar';
import { getAPIurl } from '../utils';

// HTTP эндпоинты
const httpAuthLink = new HttpLink({
  uri: `${apiUrl().proto}//${apiUrl().url}registrado/`,
});
const httpDefaultLink = new HttpLink({
  uri: `${apiUrl().proto}//${apiUrl().url}jwt/`,
});

// WebSocket эндпоинт
const wsLink = new WebSocketLink({
  uri: `${apiUrl().wsProto}${apiUrl().url}ws/`,
  options: {
    reconnect: true,
    connectionParams: wsConnectionParams(),
    connectionCallback: async (errors, result) => {
      console.log('Websocket Result: ', result);
      if (errors) {
        console.log('Websocket Errors : ', errors);
      }
    },
  },
});

// Middleware для добавления JWT токена
const defaultMiddleware = new ApolloLink((operation, forward) => {
  operation.setContext({
    headers: headers(true),
  });
  return forward(operation);
});
const authMiddleware = new ApolloLink((operation, forward) => {
  operation.setContext({
    headers: headers(),
  });
  return forward(operation);
});

// Обработка ошибок
const errorLink = onError(({ graphQLErrors, networkError }) => {
  if (graphQLErrors) {
    graphQLErrors.forEach(({ message, locations, path }) =>
      console.log(
        `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`
      )
    );
  }
  if (networkError) {
    console.log(`[Network error]: ${networkError}`);
  }
});
// Разделение ссылок для запросов и подписок
const splitLink = split(
  ({ query }) => {
    const definition = getMainDefinition(query);
    return (
      definition.kind === 'OperationDefinition' &&
      definition.operation === 'subscription'
    );
  },
  wsLink,
  ApolloLink.from([defaultMiddleware, errorLink, httpDefaultLink])
);

// Создание экземпляров Apollo Client
const authApolloClient = new ApolloClient({
  link: ApolloLink.from([authMiddleware, errorLink, httpAuthLink]),
  cache: new InMemoryCache(),
});

const defaultApolloClient = new ApolloClient({
  link: splitLink,
  cache: new InMemoryCache(),
});
const apollo: Record<string, ApolloClient<unknown>> = {
  default: defaultApolloClient,
  registrado: authApolloClient,
};
export default boot(({ app }) => {
  app.provide(ApolloClients, apollo);
});

export { apollo, authApolloClient, defaultApolloClient };

//Формирование заголовков
interface Headers {
  'X-CSRFTOKEN': string | null;
  'X-AUTH-TOKEN': string | null;
  Authorization?: string | null;
}
function headers(addJwt = false): Headers {
  const obj: Headers = {
    'X-CSRFTOKEN':
      SessionStorage.getItem('csrf_token') ||
      LocalStorage.getItem('csrf_token') ||
      null,
    'X-AUTH-TOKEN':
      SessionStorage.getItem('auth_token') ||
      LocalStorage.getItem('auth_token') ||
      null,
  };
  const jwt: string | null =
    SessionStorage.getItem('jwt_token') ||
    LocalStorage.getItem('jwt_token') ||
    null;
  if (jwt != null && addJwt) {
    obj['Authorization'] = `JWT ${jwt}`;
  }
  return obj;
}

//Формирование параметров для websocket
function wsConnectionParams() {
  const jwt =
    LocalStorage.getItem('jwtToken') === 'null'
      ? null
      : LocalStorage.getItem('jwtToken');
  return jwt ? { authorization: `JWT ${jwt}` } : {};
}

function apiUrl(): { url: string; proto: string; wsProto: string | null } {
  //Формирование путей к эндпоинтам
  const urlFull = new URL(getAPIurl());
  const url = `${urlFull.host}${urlFull.pathname}`;
  const urlProtocol: string = urlFull.protocol;
  const wsProtocol: string | null =
    urlProtocol === 'http:'
      ? 'ws://'
      : urlProtocol === 'https:'
      ? 'wss://'
      : null;
  return { url, proto: urlProtocol, wsProto: wsProtocol };
}
