import { List as ImmutableList } from 'immutable';

import { debounce } from 'lodash';

import api from '../api';
import { compareId } from '../compare_id';

export const MARKERS_FETCH_REQUEST = 'MARKERS_FETCH_REQUEST';
export const MARKERS_FETCH_SUCCESS = 'MARKERS_FETCH_SUCCESS';
export const MARKERS_FETCH_FAIL    = 'MARKERS_FETCH_FAIL';
export const MARKERS_SUBMIT_SUCCESS = 'MARKERS_SUBMIT_SUCCESS';

export const synchronouslySubmitMarkers = () => (dispatch, getState) => {
  const accessToken = getState().getIn(['meta', 'access_token'], '');
  const params      = _buildParams(getState());

  if (Object.keys(params).length === 0 || accessToken === '') {
    return;
  }

  // The Fetch API allows us to perform requests that will be carried out
  // after the page closes. But that only works if the `keepalive` attribute
  // is supported.
  if (window.fetch && 'keepalive' in new Request('')) {
    fetch('/api/v1/markers', {
      keepalive: true,
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${accessToken}`,
      },
      body: JSON.stringify(params),
    });

    return;
  } else if (navigator && navigator.sendBeacon) {
    // Failing that, we can use sendBeacon, but we have to encode the data as
    // FormData for DoorKeeper to recognize the token.
    const formData = new FormData();

    formData.append('bearer_token', accessToken);

    for (const [id, value] of Object.entries(params)) {
      formData.append(`${id}[last_read_id]`, value.last_read_id);
    }

    if (navigator.sendBeacon('/api/v1/markers', formData)) {
      return;
    }
  }

  // If neither Fetch nor sendBeacon worked, try to perform a synchronous
  // request.
  try {
    const client = new XMLHttpRequest();

    client.open('POST', '/api/v1/markers', false);
    client.setRequestHeader('Content-Type', 'application/json');
    client.setRequestHeader('Authorization', `Bearer ${accessToken}`);
    client.send(JSON.stringify(params));
  } catch (e) {
    // Do not make the BeforeUnload handler error out
  }
};

const _buildParams = (state) => {
  const params = {};

  const lastHomeId         = state.getIn(['timelines', 'home', 'items'], ImmutableList()).find(item => item !== null);
  const lastNotificationId = state.getIn(['notifications', 'lastReadId']);

  if (lastHomeId && compareId(lastHomeId, state.getIn(['markers', 'home'])) > 0) {
    params.home = {
      last_read_id: lastHomeId,
    };
  }

  if (lastNotificationId && compareId(lastNotificationId, state.getIn(['markers', 'notifications'])) > 0) {
    params.notifications = {
      last_read_id: lastNotificationId,
    };
  }

  return params;
};

const debouncedSubmitMarkers = debounce((dispatch, getState) => {
  const accessToken = getState().getIn(['meta', 'access_token'], '');
  const params      = _buildParams(getState());

  if (Object.keys(params).length === 0 || accessToken === '') {
    return;
  }

  api(getState).post('/api/v1/markers', params).then(() => {
    dispatch(submitMarkersSuccess(params));
  }).catch(() => {});
}, 300000, { leading: true, trailing: true });

export function submitMarkersSuccess({ home, notifications }) {
  return {
    type: MARKERS_SUBMIT_SUCCESS,
    home: (home || {}).last_read_id,
    notifications: (notifications || {}).last_read_id,
  };
}

export function submitMarkers(params = {}) {
  const result = (dispatch, getState) => debouncedSubmitMarkers(dispatch, getState);

  if (params.immediate === true) {
    debouncedSubmitMarkers.flush();
  }

  return result;
}

export const fetchMarkers = () => (dispatch, getState) => {
  const params = { timeline: ['notifications'] };

  dispatch(fetchMarkersRequest());

  api(getState).get('/api/v1/markers', { params }).then(response => {
    dispatch(fetchMarkersSuccess(response.data));
  }).catch(error => {
    dispatch(fetchMarkersFail(error));
  });
};

export function fetchMarkersRequest() {
  return {
    type: MARKERS_FETCH_REQUEST,
    skipLoading: true,
  };
}

export function fetchMarkersSuccess(markers) {
  return {
    type: MARKERS_FETCH_SUCCESS,
    markers,
    skipLoading: true,
  };
}

export function fetchMarkersFail(error) {
  return {
    type: MARKERS_FETCH_FAIL,
    error,
    skipLoading: true,
    skipAlert: true,
  };
}