import { put, takeEvery, all, call, select } from 'redux-saga/effects';

// Apis from parent
import { api } from 'store/apis';
// Action type
import { StoreAction } from 'store/utils/actions';
import { apolloCall } from 'store/utils/sagas';

import { config, zeus } from 'config/general';
import gql from 'graphql-tag';
import unflatten from 'unflatten';
import { gqlConvert, filterUpdateData } from 'store/utils/adminHelpers';
import { deepOmit } from 'store/utils/helper';
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client';
import fetch from 'cross-fetch';
import actions from './actions';
import selectors from './selectors';

const cache = new InMemoryCache();

const client = new ApolloClient({
  link: createHttpLink({
    uri: config.api.baseUrlSettings || config.api.baseUrlApollo || '',
    fetch,
  }),
  cache,
});

const { Zeus, $ } = zeus;

function* loadData(action) {
  const { options } = action.payload;

  const fields = action.payload.headers.reduce(
    (prev, current) => {
      if (current.key) {
        prev[current.key] = true;
      }
      if (current.id) {
        prev[current.id] = true;
      }
      return prev;
    },
    {
      _id: true,
    },
  );

  const outputModel = unflatten(fields);

  const zeusQuery = Zeus.query({
    [options.listMethod]: [{ pagination: { limit: 100, skip: 0 } }, outputModel],
  });

  const query = gql`
    ${zeusQuery}
  `;

  yield call(apolloCall, {
    query,
    actionRes: actions.load.result.action,
    client,
  });
}

function* loadOneData(action) {
  const { options, apiInput } = action.payload;

  const outputModel = gqlConvert(options.model, options);

  const zeusQuery = Zeus.query({
    [options.detailMethod]: [apiInput || {}, outputModel],
  });

  const query = gql`
    ${zeusQuery}
  `;

  yield call(apolloCall, {
    query,
    objectReturn: options.detailMethod,
    actionRes: actions.loadOne.result.action,
    callback: action.payload.callback,
    client,
  });
}

function* addData(action) {
  const { options } = action.payload;

  const outputModel = gqlConvert(options.model, options);

  const zeusMutation = Zeus.mutation({
    [options.addMethod]: [
      {
        input: $`data`,
      },
      outputModel,
    ],
  });

  const mutate = gql`
    ${zeusMutation}
  `;

  yield call(apolloCall, {
    mutate,
    objectReturn: options.addMethod,
    data: action.payload,
    actionRes: actions.add.result.action,
    callback: action.payload.callback,
    client,
  });
}

function* editData(action) {
  const { options } = action.payload;

  const outputModel = gqlConvert(options.model, options);

  const zeusMutation = Zeus.mutation({
    [options.editMethod]: [
      {
        id: $`id`,
        input: $`data`,
      },
      outputModel,
    ],
  });

  const mutate = gql`
    ${zeusMutation}
  `;

  const filteredData = filterUpdateData(options.model, action.payload.data);

  const data = filteredData;

  yield call(apolloCall, {
    mutate,
    objectReturn: options.editMethod,
    data: { ...action.payload, data: deepOmit(data, ['_id', '__typename']) },
    actionRes: actions.edit.result.action,
    callback: action.payload.callback,
    client,
  });
}

function* deleteData(action) {
  const { options, resource } = action.payload;

  const zeusMutation = Zeus.mutation({
    [options.deleteMethod]: [
      {
        id: $`id`,
      },
      { _id: true },
    ],
  });

  const mutate = gql`
    ${zeusMutation}
  `;

  yield call(apolloCall, {
    mutate,
    objectReturn: options.deleteMethod,
    data: action.payload,
    actionRes: actions.delete.result.action,
    callback: action.payload.callback,
    client,
  });
}

function* searchData(action) {
  const { search, order, limit, options } = action.payload;

  const selectData = yield select(selectors.getAllSettings);

  const currentData =
    selectData && options && options.resourceList && selectData[options.resourceList];

  const paginationData = {
    sort: order || config.defaultSort,
    skip: parseInt((currentData && currentData.data && currentData.data.length) || 0, 10),
    limit: parseInt(limit || config.defaultLimit, 10),
  };

  const fields = options.headers.reduce(
    (prev, current) => {
      if (current.key) {
        prev[current.key] = true;
      }
      if (current.id) {
        prev[current.id] = true;
      }
      return prev;
    },
    {
      _id: true,
    },
  );

  const outputModel = unflatten(fields);

  const zeusQuery = Zeus.query({
    [options.resourceList]: [
      {
        pagination: paginationData,
      },
      outputModel,
    ],
  });

  const query = gql`
    ${zeusQuery}
  `;

  yield call(apolloCall, {
    query,
    objectReturn: options.resourceList,
    actionRes: actions.search.result.action,
    callback: action.payload.callback,
    client,
  });
}

function* getACFieldInfo(action) {
  const zeusQuery = Zeus.query({
    ACFieldsGetAll: [{}, {}],
  });

  const query = gql`
    ${zeusQuery}
  `;

  yield call(apolloCall, {
    query,
    objectReturn: 'ACFieldsGetAll',
    actionRes: actions.acFieldInfo.result.action,
    callback: action.payload.callback,
    client,
  });
}

// Project Settings

function* getProjectSettings(action) {
  const zeusQuery = Zeus.query({
    projectSettingsGetMany: [{}, { key: true, value: true, _id: true }],
  });

  const query = gql`
    ${zeusQuery}
  `;

  yield call(apolloCall, {
    query,
    objectReturn: 'projectSettingsGetMany',
    actionRes: actions.getProjectSettings.result.action,
    callback: action && action.payload && action.payload.callback,
    client,
  });
}

function* addProjectSettings(action) {
  const zeusMutation = Zeus.mutation({
    projectSettingsAddOne: [
      {
        input: $`input`,
      },
      { key: true, value: true, _id: true },
    ],
  });

  const mutation = gql`
    ${zeusMutation}
  `;

  yield call(apolloCall, {
    mutation,
    objectReturn: 'projectSettingsAddOne',
    data: action.payload.data,
    actionRes: actions.addProjectSettings.result.action,
    callback: action && action.payload && action.payload.callback,
    client,
  });
}

function* editProjectSettings(action) {
  const zeusMutation = Zeus.mutation({
    projectSettingsEditOne: [
      {
        input: $`input`,
        id: $`id`,
      },
      { key: true, value: true, _id: true },
    ],
  });

  const mutation = gql`
    ${zeusMutation}
  `;

  yield call(apolloCall, {
    mutation,
    objectReturn: 'projectSettingsEditOne',
    data: action.payload.data,
    actionRes: actions.editProjectSettings.result.action,
    callback: action && action.payload && action.payload.callback,
    client,
  });
}

function* instagramCrawl(action) {
  const zeusMutation = Zeus.mutation({
    instagramCrawlPosts: [
      {},
      {
        _id: true,
        crawlDate: true,
        // eslint-disable-next-line
        posts: { id: true, media_url: true, permalink: true },
      },
    ],
  });

  const mutation = gql`
    ${zeusMutation}
  `;

  yield call(apolloCall, {
    mutation,
    objectReturn: 'instagramCrawlPosts',
    actionRes: actions.integrations.instagram.crawl.result.action,
    callback: action && action.payload && action.payload.callback,
    client,
  });
}

function* instagramPost(action) {
  const zeusQuery = Zeus.query({
    instagramGetPosts: [
      {},
      {
        _id: true,
        crawlDate: true,
        // eslint-disable-next-line
        posts: { id: true, media_url: true, permalink: true },
      },
    ],
  });

  const query = gql`
    ${zeusQuery}
  `;

  yield call(apolloCall, {
    query,
    objectReturn: 'instagramGetPosts',
    actionRes: actions.integrations.instagram.post.result.action,
    callback: action && action.payload && action.payload.callback,
    client,
  });
}

function* instagramToken(action) {
  const zeusMutation = Zeus.mutation({
    instagramGenerateLongToken: [
      {
        code: $`code`,
      },
      {
        message: true,
      },
    ],
  });

  const mutation = gql`
    ${zeusMutation}
  `;

  yield call(apolloCall, {
    mutation,
    objectReturn: 'instagramGenerateLongToken',
    data: action.payload,
    actionRes: actions.integrations.instagram.generate.result.action,
    callback: action && action.payload && action.payload.callback,
    client,
  });
}

function* settingsWatchers() {
  yield all([
    yield takeEvery(actions.load.request.constant, loadData),
    yield takeEvery(actions.loadOne.request.constant, loadOneData),
    yield takeEvery(actions.add.request.constant, addData),
    yield takeEvery(actions.edit.request.constant, editData),
    yield takeEvery(actions.delete.request.constant, deleteData),
    yield takeEvery(actions.search.request.constant, searchData),
    // Other
    yield takeEvery(actions.acFieldInfo.request.constant, getACFieldInfo),
    // Project Settings
    yield takeEvery(actions.getProjectSettings.request.constant, getProjectSettings),
    yield takeEvery(actions.addProjectSettings.request.constant, addProjectSettings),
    yield takeEvery(actions.editProjectSettings.request.constant, editProjectSettings),
    // Integrations - Instagram
    yield takeEvery(actions.integrations.instagram.crawl.request.constant, instagramCrawl),
    yield takeEvery(actions.integrations.instagram.post.request.constant, instagramPost),
    yield takeEvery(actions.integrations.instagram.generate.request.constant, instagramToken),
  ]);
}

export default function* saga() {
  yield call(settingsWatchers);
}
