import { useState, useEffect } from "react";
import { getPluckyClient } from "utils";
import {
  GetOccurrencesResponse,
  GetItemsResponse,
  Plucky,
} from "plucky_client/plucky";
import { useLocation } from "react-router-dom";

export interface QueryResponse<TData> {
  data: TData | null;
  loading: boolean;
  error: Error | null;
}

export function useItems(
  serviceID: string,
  stageID: string,
  start: Date,
  end: Date
): QueryResponse<GetItemsResponse> {
  return useCloudwatchLogsQuery(
    function (
      client: Plucky,
      skipPartialDataRefetch: boolean
    ): Promise<GetItemsResponse> {
      return client.getItems({
        serviceId: serviceID,
        stageId: stageID,
        start,
        end,
        skipPartialDataRefetch,
        maxTrendPoints: 20,
      });
    },
    [serviceID, start.toISOString(), end.toISOString()]
  );
}

export function useOccurrences(
  serviceID: string,
  stageID: string,
  fingerprint: string,
  start: Date,
  end: Date
): QueryResponse<GetOccurrencesResponse> {
  return useCloudwatchLogsQuery(
    function (
      client: Plucky,
      skipPartialDataRefetch: boolean
    ): Promise<GetOccurrencesResponse> {
      return client.getOccurrences({
        serviceId: serviceID,
        stageId: stageID,
        fingerprint,
        start,
        end,
        skipPartialDataRefetch,
      });
    },
    [serviceID, fingerprint, start.toISOString(), end.toISOString()]
  );
}

// The location state that Links should push to indicate that page can skip
// querying Cloudwatch Logs, and serve cached data.
export interface CloudwatchLogsQueryLocationState {
  skipPartialDataRefetch: boolean;
}

type MaybeCloudwatchLogsQueryLocationState =
  | CloudwatchLogsQueryLocationState
  | undefined
  | null;

interface CloudwatchLogsQueryState<TData> extends QueryResponse<TData> {
  isFirstFetch: boolean;
}

// The Plucky server caches data that it fetches from CloudWatch Logs, and its API supports
//   * Refetching from CloudWatch logs to get events that were not indexed yet
//   * Only serving from cached data, so that results can be returned faster
//
// useCloudwatchLogsQuery gives us a way to use these APIs, and it handles determining
// whether the server should refetch from CloudWatch Logs or not.
//
// We want to allow fetching from CloudWatch Logs when:
//   * The app is first loading data for a given service - fresh page load
//   * The app
function useCloudwatchLogsQuery<TData>(
  queryFunc: (
    client: Plucky,
    skipPartialDataRefetch: boolean
  ) => Promise<TData>,
  dependencies: any[]
): QueryResponse<TData> {
  const [state, setState] = useState<CloudwatchLogsQueryState<TData>>({
    data: null,
    loading: false,
    error: null,
    isFirstFetch: true,
  });

  // Check whether we got to this page via a client-side route change or a full page load.
  const location = useLocation<MaybeCloudwatchLogsQueryLocationState>();
  const isClientSideRouteChange = !!(
    location.state && location.state.skipPartialDataRefetch
  );

  // Determine whether we need to load the latest data from Cloudwatch Logs (which is slow),
  // or we can use data cached by the server (which is much faster).
  let skipPartialDataRefetch: boolean;
  if (state.isFirstFetch) {
    // This invocation was triggered by loading the page.
    // If this was due to a client-side route change, we can assume that the previous page loaded
    // data into the cache.
    skipPartialDataRefetch = isClientSideRouteChange;
  } else {
    // If this invocation isn't from the page first loading, then it was triggered by an
    // explicit request to load the latest data.
    skipPartialDataRefetch = false;
  }

  useEffect(() => {
    const runQuery = async function () {
      const pluckyClient = getPluckyClient();
      setState({
        ...state,
        loading: true,
      });

      try {
        const data = await queryFunc(pluckyClient, skipPartialDataRefetch);
        setState({
          ...state,
          loading: false,
          data,
          error: null,
          isFirstFetch: false,
        });
      } catch (error) {
        setState({
          ...state,
          loading: false,
          error,
          isFirstFetch: false,
        });
      }
    };

    runQuery();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, dependencies);

  return state;
}
