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

// Apis from parent
import { api } from 'store/apis';
// Action type
import { StoreAction } from 'store/utils/actions';
import { apolloCall, customFetch, genericApolloSaga } from 'store/utils/apollo';

import { config } from 'config/general';

import gql from 'graphql-tag';
import { ApolloClient, createHttpLink, InMemoryCache } from '@apollo/client';
import fetch from 'cross-fetch';
import app from 'store/app';
import apiCall from 'store/utils/sagas';
import { dynamicApi } from 'store/utils/api';
import actions from './actions';
import selectors from './selectors';
import * as zeus from './graphql-zeus';
import { myFileModel, fileUrlModel } from './models';

const { Zeus, $ } = zeus;

const cache = new InMemoryCache();

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

function* uploadFile(action) {
  const payload = (action && action.payload) || {};
  const mutate = gql`
    mutation upload($data: NewFileInput!) {
      uploadFile(input: $data) {
        _id
        title
        fileType
        large
        medium
        small
      }
    }
  `;

  console.log('payload', payload);

  yield call(apolloCall, {
    mutate,
    objectReturn: 'uploadFile',
    client,
    variables: {
      data: payload.data,
    },
    callback: payload.callback,
    callbackError: payload.callbackError,
    progress: payload.progress,
    noLoading: payload.noLoading,
    loaderInfo: { upload: true },
    noAppHeaders: true,
  });
}

function* loadListFile() {
  const query = gql`
    {
      myFiles(pagination: { limit: 100, skip: 0 }) {
        _id
        title
        fileType
        large
        medium
        small
      }
    }
  `;

  yield call(apolloCall, {
    query,
    objectReturn: 'myFiles',
    actionRes: actions.list.result.action,
    noLoading: true,
    client,
    noAppHeaders: true,
  });
}

/**
 * Flow :
 * 1. Get upload URL
 * 2. Upload to S3
 * 3. Get file
 */
function* uploadS3(action: StoreAction) {
  const payload = action?.payload || {};
  const data = payload.data;

  const mutationObj = {};

  // 1. Get file upload URL

  // case multiple files at once
  if (Array.isArray(data)) {
    mutationObj.__alias = {};
    for (let index = 0; index < data.length; index++) {
      mutationObj.__alias[`filesInitUpload${index}`] = {
        filesInitUpload: [
          {
            input: $`input` + index,
          },
          fileUrlModel,
        ],
      };
    }
  }
  // case one file
  else {
    mutationObj.filesInitUpload = [
      {
        input: $`input`,
      },
      fileUrlModel,
    ];
  }

  const zeusMutation = Zeus.mutation(mutationObj);
  const mutation = gql`
    ${zeusMutation}
  `;

  // mutation data
  const mutationData = {};

  const fileData = item => ({
    ressourceModel: item.ressourceModel,
    ressourceId: item.ressourceId,
    fileName: item.fileName,
    privateFile: item.privateFile,
    tagsIds: item.tagsId,
  });

  if (Array.isArray(data)) {
    for (let index = 0; index < data.length; index++) {
      const item = data[index];
      mutationData[`input${index}`] = fileData(item);
    }
  } else {
    mutationData.input = fileData(data);
  }

  // Graph call
  const responseUrl = yield call(apolloCall, {
    noLoading: payload.noLoading,
    mutation,
    client,
    data: mutationData,
    callbackError: payload.callbackError,
  });

  let continueProcess = false;

  // 2. Upload the images on the url(s) received
  if (Array.isArray(data)) {
    const apiCalls = [];

    for (let index = 0; index < data.length; index++) {
      const item = data[index];
      // syntax for response aliases
      const url = responseUrl?.[`filesInitUpload__alias__filesInitUpload${index}`]?.uploadUrl;
      if (!url) continue;

      continueProcess = true;

      const uploadApi = dynamicApi(url, 'PUT');
      apiCalls.push(
        call(apiCall, {
          // ignore api option
          api: uploadApi,
          data: {
            file: item.file,
            name: item.fileName,
          },
          noLoading: payload.noLoading,
          fileUploadS3: true,
          raw: true,
          noToken: true,
          progress: event => payload.progress(event, index),
          callbackError: err => payload.callbackError(err, index),
        }),
      );
    }

    if (apiCalls.length) yield all(apiCalls);
  } else {
    const url = responseUrl?.filesInitUpload?.uploadUrl;

    if (!url) return;

    const uploadApi = dynamicApi(url, 'PUT');

    yield call(apiCall, {
      // ignore api option
      api: uploadApi,
      data: {
        file: data.file,
        name: data.fileName,
      },
      noLoading: payload.noLoading,
      fileUploadS3: true,
      raw: true,
      noToken: true,
      progress: event => payload.progress(event),
      callbackError: err => payload.callbackError(err),
    });

    continueProcess = true;
  }

  // avoid going further if error
  if (!continueProcess) return;

  // 3. Get the uploaded file(s)
  const queryObj = {};

  if (Array.isArray(data)) {
    queryObj.__alias = {};
    for (let index = 0; index < data.length; index++) {
      const item = data[index];
      const id = responseUrl?.[`filesInitUpload__alias__filesInitUpload${index}`]?.file?._id;

      queryObj.__alias[`filesGetOne${index}`] = {
        filesGetOne: [
          {
            id,
          },
          myFileModel(item.size),
        ],
      };
    }
  } else {
    const id = responseUrl?.filesInitUpload?.file?._id;

    queryObj.filesGetOne = [
      {
        id,
      },
      myFileModel(data.size),
    ];
  }

  const zeusQuery = Zeus.query(queryObj);

  const query = gql`
    ${zeusQuery}
  `;

  // 3. Get File url
  const responseFile = yield call(apolloCall, {
    noLoading: payload.noLoading,
    client,
    query,
    callbackError: payload.callbackError,
  });

  let result;

  if (Array.isArray(data)) {
    result = [];

    for (let index = 0; index < data.length; index++) {
      const file = responseFile?.[`filesGetOne__alias__filesGetOne${index}`];
      // important to not have wrong file update
      if (!file) result[index] = null;

      result[index] = file;
    }
  } else {
    result = responseFile?.filesGetOne;
  }

  if (typeof payload.callback === 'function') {
    payload.callback(result);
  }

  yield put(actions.uploadS3.result.action(result, 'fulfilled'));
}

function* filesGetMine(action: StoreAction) {
  const payload = action?.payload || {};

  const search = payload.data || {};

  if (payload.limit) {
    search.pagination = {
      limit: payload.limit,
      skip: ((payload.page || 1) - 1) * payload.limit,
    };
  }

  const queryObj = {
    filesGetMine: [search, myFileModel(payload.size)],
  };

  if (payload.limit) {
    queryObj.filesGetMineCount = [search];
  }

  const zeusQuery = Zeus.query(queryObj);

  const query = gql`
    ${zeusQuery}
  `;

  payload.query = query;
  payload.client = client;

  yield call(genericApolloSaga('', actions.filesGetMine.result.action), action);
}

function* filesGetOne(action: StoreAction) {
  const payload = action?.payload || {};

  const queryObj = {
    filesGetOne: [
      {
        id: payload.id,
      },
      myFileModel(payload.size),
    ],
  };

  const zeusQuery = Zeus.query(queryObj);

  const query = gql`
    ${zeusQuery}
  `;

  payload.query = query;
  payload.client = client;

  return yield call(genericApolloSaga('filesGetOne', actions.filesGetOne.result.action), action);
}

function* fileWatchers() {
  yield all([
    takeEvery(actions.list.request.constant, loadListFile),
    takeEvery(actions.upload.request.constant, uploadFile),
    takeEvery(actions.uploadS3.request.constant, uploadS3),
    takeEvery(actions.filesGetMine.request.constant, filesGetMine),
    takeEvery(actions.filesGetOne.request.constant, filesGetOne),
  ]);
}

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