import { isEqual } from "lodash";
import { Dispatch } from "redux";

import { push } from "connected-react-router";
import { ISearchFilter } from "../searchFilters";
import { getFromApi } from "../util";
import { ActionType, IAppState, IGraph, IInstanceDetails, IInstanceSort, ISearchResponse } from "./types";

// Instance details
const requestInstanceDetails = (instanceName: string) => {
  return {
    payload: instanceName,
    type: ActionType.REQUEST_INSTANCE_DETAILS
  };
};
const receiveInstanceDetails = (instanceDetails: IInstanceDetails) => {
  return {
    payload: instanceDetails,
    type: ActionType.RECEIVE_INSTANCE_DETAILS
  };
};
const instanceLoadFailed = () => {
  return {
    type: ActionType.INSTANCE_LOAD_ERROR
  };
};
const deselectInstance = () => {
  return {
    type: ActionType.DESELECT_INSTANCE
  };
};

// Graph
const requestGraph = () => {
  return {
    type: ActionType.REQUEST_GRAPH
  };
};
const receiveGraph = (graph: IGraph) => {
  return {
    payload: graph,
    type: ActionType.RECEIVE_GRAPH
  };
};
const graphLoadFailed = () => {
  return {
    type: ActionType.GRAPH_LOAD_ERROR
  };
};

// Instance list
const requestInstanceList = (sort?: IInstanceSort) => ({
  payload: sort,
  type: ActionType.REQUEST_INSTANCES
});
const receiveInstanceList = (instances: IInstanceDetails[]) => ({
  payload: instances,
  type: ActionType.RECEIVE_INSTANCES
});
const instanceListLoadFailed = () => ({
  type: ActionType.INSTANCE_LIST_LOAD_ERROR
});

// Search
const requestSearchResult = (query: string, filters: ISearchFilter[]) => {
  return {
    payload: { query, filters },
    type: ActionType.REQUEST_SEARCH_RESULTS
  };
};
const receiveSearchResults = (result: ISearchResponse) => {
  return {
    payload: result,
    type: ActionType.RECEIVE_SEARCH_RESULTS
  };
};
const searchFailed = () => {
  return {
    type: ActionType.SEARCH_RESULTS_ERROR
  };
};

const resetSearch = () => {
  return {
    type: ActionType.RESET_SEARCH
  };
};

export const setResultHover = (domain?: string) => {
  return {
    payload: domain,
    type: ActionType.SET_SEARCH_RESULT_HOVER
  };
};

/** Async actions: https://redux.js.org/advanced/asyncactions */

export const loadInstance = (instanceName: string | null) => {
  return (dispatch: Dispatch, getState: () => IAppState) => {
    if (!instanceName) {
      dispatch(deselectInstance());
      if (getState().router.location.pathname.startsWith("/instance/")) {
        dispatch(push("/"));
      }
      return;
    }
    dispatch(requestInstanceDetails(instanceName));
    return getFromApi("instances/" + instanceName)
      .then(details => dispatch(receiveInstanceDetails(details)))
      .catch(() => dispatch(instanceLoadFailed()));
  };
};

export const updateSearch = (query: string, filters: ISearchFilter[]) => {
  return (dispatch: Dispatch, getState: () => IAppState) => {
    query = query.trim();

    if (!query) {
      dispatch(resetSearch());
      return;
    }

    const prevQuery = getState().search.query;
    const prevFilters = getState().search.filters;
    const isNewQuery = prevQuery !== query || !isEqual(prevFilters, filters);

    const next = getState().search.next;
    let url = `search/?query=${query}`;
    if (!isNewQuery && next) {
      url += `&after=${next}`;
    }

    // Add filters
    // The format is e.g. type_eq=mastodon or user_count_gt=1000
    filters.forEach(filter => {
      url += `&${filter.field}_${filter.relation}=${filter.value}`;
    });

    dispatch(requestSearchResult(query, filters));
    return getFromApi(url)
      .then(result => dispatch(receiveSearchResults(result)))
      .catch(() => dispatch(searchFailed()));
  };
};

export const fetchGraph = () => {
  return (dispatch: Dispatch) => {
    dispatch(requestGraph());
    return getFromApi("graph")
      .then(graph => dispatch(receiveGraph(graph)))
      .catch(() => dispatch(graphLoadFailed()));
  };
};

export const loadInstanceList = (page?: number, sort?: IInstanceSort) => {
  return (dispatch: Dispatch, getState: () => IAppState) => {
    sort = sort ? sort : getState().data.instanceListSort;
    dispatch(requestInstanceList(sort));
    const params: string[] = [];
    if (!!page) {
      params.push(`page=${page}`);
    }
    if (!!sort) {
      params.push(`sortField=${sort.field}`);
      params.push(`sortDirection=${sort.direction}`);
    }
    const path = !!params ? `instances?${params.join("&")}` : "instances";
    return getFromApi(path)
      .then(instancesListResponse => dispatch(receiveInstanceList(instancesListResponse)))
      .catch(() => dispatch(instanceListLoadFailed()));
  };
};
