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

// Apis from parent
// Action type
import { StoreAction } from 'store/utils/actions';
import { apolloCall, genericApolloSaga } from 'store/utils/sagas';
import { config, zeus } from 'config/general';

import gql from 'graphql-tag';
import { pushRoute } from 'store/utils/routes';
import selectors from 'store/app/selectors';
import { push } from 'connected-react-router';
import auth from 'store/auth';
import { deepOmit } from 'store/utils/helper';
import * as models from './models';
import actions from './actions';

const { Zeus, $ } = zeus;

const treatmentOut = data => {
  const output = {
    ...data,
  };

  return deepOmit(output, [
    'addBillingInfo',
    'email',
    'payoutInfo',
    'paymentInfo',
    'profile',
    '__typename',
    'types',
    '_id',
    'createdAt',
    'updatedAt',
    'getFavorites',
  ]);
};

/**
 * Profile
 */
function* userProfile(action: StoreAction): void {
  const payload = action.payload || {};

  const zeusQuery = Zeus.query({
    me: models.accountModel,
  });

  const query = gql`
    ${zeusQuery}
  `;

  const profile = yield call(apolloCall, {
    objectReturn: 'me',
    query,
    actionRes: actions.profile.result.action,
    noLoading: payload.noLoading,
    callback: payload.callback,
    callbackError: payload.callbackError,
    noFeedback: payload.noFeedback,
  });

  return profile;
}

function* updateProfile(action: StoreAction): void {
  const payload = action.payload || {};

  const zeusMutation = Zeus.mutation({
    updateMe: [
      {
        input: $`data`,
      },
      models.accountModel,
    ],
  });

  const mutate = gql`
    ${zeusMutation}
  `;

  yield call(apolloCall, {
    mutate,
    objectReturn: 'updateMe',
    variables: {
      data: treatmentOut(payload.data),
    },
    callback: payload.callback,
    success: payload.success,
    actionRes: actions.updateProfile.result.action,
  });
}

export function* userInit(): void {
  const profile = yield call(userProfile, {});
  return profile;
}

function* reloadProfile(action: StoreAction) {
  if (action.status !== 'fulfilled') return;

  yield call(userProfile, {
    noLoading: true,
  });
}

export function* afterLogin(action) {
  const lg = yield select(state => state.content.lg);
  const profile = yield call(userInit);
  const redirect = yield select(state => state.account.redirect);

  if (!profile.firstName) {
    yield put(push(pushRoute('register.step2', lg)));
    return profile;
  }

  if (redirect) {
    yield put(actions.saveRedirect.action(null));
    yield put(push(redirect));
    return profile;
  }

  yield put(push(pushRoute('home', lg)));

  return profile;
}

export function* afterSignup(action) {
  const payload = action.payload || {};

  yield put(auth.actions.login.request.action(payload));
  yield take(auth.actions.login.result.constant);

  yield call(afterLogin);
}

/**
 * Sign-up steps
 */
function* register(action: StoreAction): void {
  const payload = action.payload || {};

  const zeusMutation = Zeus.mutation({
    register: [
      {
        input: $`data`,
      },
      models.accountModel,
    ],
  });

  const mutate = gql`
    ${zeusMutation}
  `;

  yield call(apolloCall, {
    mutate,
    objectReturn: 'register',
    variables: {
      data: payload.data,
    },
    success: payload.success,
    callback: payload.callback,
    actionRes: actions.updateProfile.result.action,
  });
}

function* changePassword(action: StoreAction): void {
  const payload = action.payload || {};

  const zeusMutation = Zeus.mutation({
    updateMePassword: [
      {
        input: $`data`,
      },
      models.accountModel,
    ],
  });

  const mutate = gql`
    ${zeusMutation}
  `;

  yield call(apolloCall, {
    mutate,
    objectReturn: 'updateMePassword',
    variables: {
      data: payload.data,
    },
    success: payload.success,
    callback: payload.callback,
    actionRes: actions.changePassword.result.action,
  });
}

function* changeEmail(action: StoreAction): void {
  const payload = action.payload || {};

  const zeusMutation = Zeus.mutation({
    updateMeEmail: [
      {
        input: $`data`,
      },
      models.accountModel,
    ],
  });

  const mutate = gql`
    ${zeusMutation}
  `;

  yield call(apolloCall, {
    mutate,
    objectReturn: 'updateMeEmail',
    variables: {
      data: payload.data,
    },
    success: payload.success,
    callback: payload.callback,
    actionRes: actions.profile.result.action,
  });
}

/**
 * Billing info
 */
function* addBillingInfo(action: StoreAction): void {
  const payload = action.payload || {};

  const zeusMutation = Zeus.mutation({
    accountsBillingInfosAddOne: [
      {
        input: $`data`,
      },
      models.billingAddressModel,
    ],
  });

  const mutate = gql`
    ${zeusMutation}
  `;

  yield call(apolloCall, {
    mutate,
    objectReturn: 'accountsBillingInfosAddOne',
    variables: {
      data: deepOmit(payload.data, ['__typename']),
    },
    success: payload.success,
    callback: payload.callback,
    actionRes: actions.addBillingInfos.result.action,
  });
}

function* updateBillingInfo(action: StoreAction): void {
  const payload = action.payload || {};

  const zeusMutation = Zeus.mutation({
    accountsBillingInfosEditOne: [
      {
        input: $`data`,
        id: $`id`,
      },
      models.billingAddressModel,
    ],
  });

  const mutate = gql`
    ${zeusMutation}
  `;

  yield call(apolloCall, {
    mutate,
    objectReturn: 'accountsBillingInfosEditOne',
    variables: {
      data: deepOmit(payload.data, ['__typename', '_id']),
      id: payload.id,
    },
    success: payload.success,
    callback: payload.callback,
    actionRes: actions.updateBillingInfos.result.action,
  });
}

function* deleteBillingInfo(action: StoreAction): void {
  const payload = action.payload || {};

  const zeusMutation = Zeus.mutation({
    accountsBillingInfosDeleteOne: [
      {
        id: payload.id,
      },
      {},
    ],
  });

  const mutate = gql`
    ${zeusMutation}
  `;

  yield call(apolloCall, {
    mutate,
    objectReturn: 'accountsBillingInfosDeleteOne',
    variables: { id: payload.id },
    success: payload.success,
    callback: payload.callback,
    actionRes: actions.deleteBillingInfos.result.action,
  });
}

/**
 * add paymentMethod
 */
function* addPaymentMethod(action: StoreAction): void {
  action.payload = action.payload || {};
  const payload = action.payload;

  const zeusMutation = Zeus.mutation({
    accountsPaymentMethodsAddOne: [{ input: $`data` }, models.paymentMethodDetail],
  });

  const mutate = gql`
    ${zeusMutation}
  `;

  payload.mutation = mutate;

  payload.data = {
    data: payload.data,
  };

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

/* change default paymentMethod */
function* updateDefaultPaymentMethod(action: StoreAction): void {
  const payload = action.payload || {};

  const zeusMutation = Zeus.mutation({
    accountsPaymentMethodsMarkasDefault: [{ id: $`id` }, models.paymentMethodDetail],
  });

  const mutate = gql`
    ${zeusMutation}
  `;

  yield call(apolloCall, {
    mutate,
    objectReturn: 'accountsPaymentMethodsMarkasDefault',
    variables: {
      id: payload && payload.id,
    },
    callback: payload && payload.callback,
    actionRes: actions.updateDefaultPaymentMethod.result.action,
    success: payload && payload.success,
  });
}

/**
 * delete paymentMethod
 */
function* deletePaymentMethod(action: StoreAction): void {
  const payload = action.payload || {};

  const zeusMutation = Zeus.mutation({
    accountsPaymentMethodsDeleteOne: [{ id: $`id` }],
  });

  const mutate = gql`
    ${zeusMutation}
  `;

  yield call(apolloCall, {
    mutate,
    objectReturn: 'accountsPaymentMethodsDeleteOne',
    variables: {
      id: payload.id,
    },
    callback: payload.callack,
    actionRes: actions.deletePaymentMethod.result.action,
    success: payload.success,
  });
}

function* addFavoriteToAccount(action: StoreAction): void {
  const payload = action.payload || {};

  const zeusMutation = Zeus.mutation({
    accountsFavoritesAddOne: [{ input: $`data` }, models.favoriteModel],
  });

  const mutate = gql`
    ${zeusMutation}
  `;

  yield call(apolloCall, {
    mutate,
    objectReturn: 'accountsFavoritesAddOne',
    variables: {
      data: payload.data,
    },
    callback: payload.callback,
    actionRes: actions.addFavoriteToAccount.result.action,
    success: payload.success,
  });
}

function* removeFavoriteToAccount(action: StoreAction): void {
  const payload = action.payload || {};

  const zeusMutation = Zeus.mutation({
    accountsFavoritesRemoveOne: [{ input: $`data` }, { success: true }],
  });

  const mutate = gql`
    ${zeusMutation}
  `;

  yield call(apolloCall, {
    mutate,
    objectReturn: 'accountsFavoritesRemoveOne',
    variables: {
      data: payload.data,
    },
    callback: payload.callback,
    callbackError: payload.callbackError,
    noFeedback: payload.noFeedback,
    noLoading: payload.noLoading,
    actionRes: actions.removeFavoriteToAccount.result.action,
    success: payload.success,
  });
}

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

  const zeusMutation = Zeus.mutation({
    accountsLinkWithStripe: [],
  });

  payload.mutation = gql`
    ${zeusMutation}
  `;

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

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

  const zeusQuery = Zeus.query({
    accountsDashboardLinkStripe: [],
  });

  payload.query = gql`
    ${zeusQuery}
  `;

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

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

  const zeusMutation = Zeus.mutation({
    accountsRefreshInfoStripe: [{}, models.accountModel],
  });

  payload.mutation = gql`
    ${zeusMutation}
  `;

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

function* AccountWatchers(): void {
  yield all([
    yield takeEvery(actions.profile.request.constant, userProfile),
    yield takeEvery(actions.changeEmail.request.constant, changeEmail),
    yield takeEvery(actions.changePassword.request.constant, changePassword),
    yield takeEvery(actions.afterLogin.constant, afterLogin),
    yield takeEvery(actions.afterSignup.constant, afterSignup),
    yield takeEvery(actions.updateProfile.request.constant, updateProfile),
    // signup
    yield takeEvery(actions.register.request.constant, register),

    yield takeEvery(actions.addBillingInfos.request.constant, addBillingInfo),
    yield takeEvery(actions.updateBillingInfos.request.constant, updateBillingInfo),
    yield takeEvery(actions.deleteBillingInfos.request.constant, deleteBillingInfo),
    yield takeEvery(actions.addPaymentMethod.request.constant, addPaymentMethod),
    yield takeEvery(actions.deletePaymentMethod.request.constant, deletePaymentMethod),
    yield takeEvery(
      actions.updateDefaultPaymentMethod.request.constant,
      updateDefaultPaymentMethod,
    ),
    yield takeEvery(actions.addFavoriteToAccount.request.constant, addFavoriteToAccount),
    yield takeEvery(actions.removeFavoriteToAccount.request.constant, removeFavoriteToAccount),

    // stripe
    yield takeEvery(actions.stripeLink.request.constant, stripeLink),
    yield takeEvery(actions.stripeRefresh.request.constant, stripeRefresh),
    yield takeEvery(actions.stripeDashboardLink.request.constant, stripeDashboardLink),

    // reload
    yield takeEvery(actions.addBillingInfos.result.constant, reloadProfile),
    yield takeEvery(actions.updateBillingInfos.result.constant, reloadProfile),
    yield takeEvery(actions.deleteBillingInfos.result.constant, reloadProfile),
    yield takeEvery(actions.addPaymentMethod.result.constant, reloadProfile),
    yield takeEvery(actions.deletePaymentMethod.result.constant, reloadProfile),
    yield takeEvery(actions.updateDefaultPaymentMethod.result.constant, reloadProfile),
    yield takeEvery(actions.addFavoriteToAccount.result.constant, reloadProfile),
    yield takeEvery(actions.removeFavoriteToAccount.result.constant, reloadProfile),
  ]);
}

export default function* saga(): void {
  yield call(AccountWatchers);
}
