import { code } from 'dashboard/generated/controlplane';
import {
  createEventStreams,
  createEventTypes,
  createServices,
} from 'dashboard/generated/controlplane/twirp';
import * as _ from 'lodash';
import { TwirpHTTPError } from 'pbjs-twirp';
import { useEffect, useState } from 'react';
import { GetControlplaneEndpoint } from './env';

import twirp = code.justin.tv.eventbus.controlplane;

export const useEventTypes = (ldapGroup: string | null) => {
  const [eventTypes, setEventTypes] = useState<Array<twirp.IEventType>>(
    new Array<twirp.EventType>(),
  );
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  const listEventTypesReq = new twirp.ListEventTypesReq(
    ldapGroup === null
      ? {}
      : {
          ldapGroup,
        },
  );
  const client = createEventTypes(GetControlplaneEndpoint());

  useEffect(() => {
    const fetchData = async () => {
      try {
        const resp = await client.list(listEventTypesReq);
        setEventTypes(resp.eventTypes);
        setLoading(false);
      } catch (err) {
        setLoading(false);
        setError(err);
      }
    };
    fetchData();
  }, []);

  return { eventTypes, loading, error };
};

export const useEventStream = (eventTypeName: string, environment: string) => {
  const [eventStream, setEventStream] = useState<twirp.IEventStream | null>(
    null,
  );
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  const getEventTypeReq = new twirp.GetEventStreamReq({
    eventType: eventTypeName,
    environment, // this doesnt matter
  });
  const client = createEventStreams(GetControlplaneEndpoint());

  useEffect(() => {
    const fetchData = async () => {
      try {
        const resp = await client.get(getEventTypeReq);
        setEventStream(resp);
        setLoading(false);
      } catch (err) {
        setLoading(false);
        setError(err);
      }
    };
    fetchData();
  }, []);

  return { eventStream, loading, error };
};

export const useAuthorizedFieldSubscriberGrants = (eventTypeName: string) => {
  const [authorizedSubscriberDetails, setAuthSubDetails] = useState<
    Array<twirp.IGrant>
  >(new Array<twirp.IGrant>());
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);
  const [notFound, setNotFound] = useState(false);
  const [forbidden, setForbidden] = useState(false);
  const [reloadToggle, setReloadToggle] = useState(false);
  const reload = () => {
    setReloadToggle(!reloadToggle);
  };

  const req = new twirp.ListAuthorizedFieldSubscriberGrantsByEventTypeReq({
    eventTypeName,
  });
  const client = createEventTypes(GetControlplaneEndpoint());

  useEffect(() => {
    const fetchData = async () => {
      try {
        const resp = await client.listAuthorizedFieldSubscriberGrantsByEventType(
          req,
        );
        setAuthSubDetails(resp.grants);
        setLoading(false);
      } catch (err) {
        if (
          err instanceof TwirpHTTPError &&
          (err as TwirpHTTPError).status === 403
        ) {
          setForbidden(true);
          setLoading(false);
        } else if (
          err instanceof TwirpHTTPError &&
          (err as TwirpHTTPError).status === 404
        ) {
          setNotFound(true);
          setLoading(false);
        } else {
          setError(err);
          setLoading(false);
        }
      }
    };
    fetchData();
  }, [reloadToggle]);

  return {
    authorizedSubscriberDetails,
    loading,
    error,
    forbidden,
    notFound,
    reload,
  };
};

export const useAuthorizedFieldPublisherGrants = (eventTypeName: string) => {
  const [authorizedPublisherDetails, setAuthPubDetails] = useState<
    Array<twirp.IGrant>
  >(new Array<twirp.IPublisherGrant>());
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);
  const [notFound, setNotFound] = useState(false);
  const [forbidden, setForbidden] = useState(false);

  const req = new twirp.ListAuthorizedFieldPublisherGrantsByEventTypeReq({
    eventTypeName,
  });
  const client = createEventTypes(GetControlplaneEndpoint());

  useEffect(() => {
    const fetchData = async () => {
      try {
        const resp = await client.listAuthorizedFieldPublisherGrantsByEventType(
          req,
        );
        setAuthPubDetails(resp.grants);
        setLoading(false);
      } catch (err) {
        if (
          err instanceof TwirpHTTPError &&
          (err as TwirpHTTPError).status === 403
        ) {
          setForbidden(true);
          setLoading(false);
        } else if (
          err instanceof TwirpHTTPError &&
          (err as TwirpHTTPError).status === 404
        ) {
          setNotFound(true);
          setLoading(false);
        } else {
          setError(err);
          setLoading(false);
        }
      }
    };
    fetchData();
  }, []);

  return {
    authorizedPublisherDetails,
    setAuthPubDetails,
    loading,
    error,
    forbidden,
    notFound,
  };
};

export const useEventStreams = (eventType?: string) => {
  const [eventStreams, setEventStreams] = useState<Array<twirp.IEventStream>>(
    new Array<twirp.EventStream>(),
  );
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  const listEventStreamsReq = new twirp.ListEventStreamsReq(
    eventType ? { eventTypeName: eventType } : {},
  );

  const client = createEventStreams(GetControlplaneEndpoint());

  useEffect(() => {
    const fetchData = async () => {
      try {
        const resp = await client.list(listEventStreamsReq);
        setEventStreams(resp.eventStreams);
        setLoading(false);
      } catch (err) {
        setLoading(false);
        setError(err);
      }
    };
    fetchData();
  }, []);

  return { eventStreams, loading, error };
};

export const useProductionEventStreams = () => {
  const { eventStreams, loading, error } = useEventStreams();

  const productionEventStreams = _.filter(
    eventStreams,
    (eventStream: twirp.IEventStream) =>
      eventStream.environment === 'production',
  );

  return { productionEventStreams, loading, error };
};

export const useEventStreamStats = (eventType: string, environment: string) => {
  const [maxEPM, setMaxEPM] = useState<number | null>(null);
  const [minEPM, setMinEPM] = useState<number | null>(null);
  const [meanEPM, setMeanEPM] = useState<number | null>(null);
  const [meanSize, setMeanSize] = useState<number | null>(null);
  const [publishers, setPublishers] = useState<twirp.IPublisherServiceInfo[]>(
    new Array<twirp.IPublisherServiceInfo>(),
  );

  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);

  const client = createEventStreams(GetControlplaneEndpoint());

  const req = new twirp.GetEventStreamStatsReq({
    environment,
    eventType,
  });

  useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true);
        const stats = await client.getStats(req);
        setMaxEPM(_.get(stats, 'maxEventsPerMinute.value', null));
        setMinEPM(_.get(stats, 'minEventsPerMinute.value', null));
        setMeanEPM(_.get(stats, 'meanEventsPerMinute.value', null));
        setMeanSize(_.get(stats, 'meanEventPayloadSize.value', null));
        setPublishers(stats.publishers);
        setLoading(false);
      } catch (e) {
        setError(e);
        setLoading(false);
      }
    };
    fetchData();
  }, [eventType, environment]);

  return {
    maxEPM,
    minEPM,
    meanEPM,
    meanSize,
    publishers,
    loading,
    error,
  };
};

export const useEventTypeSubscribers = (eventType: string) => {
  const [subscribers, setSubscribers] = useState<twirp.IService[]>(
    new Array<twirp.IService>(),
  );
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);

  const client = createServices(GetControlplaneEndpoint());

  const req = new twirp.GetServicesForEventStreamReq({
    eventType,
  });

  useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true);
        const resp = await client.getServicesForEventStream(req);
        setSubscribers(resp.services);
        setLoading(false);
      } catch (e) {
        setError(e);
        setLoading(false);
      }
    };
    fetchData();
  }, [eventType]);

  return {
    subscribers,
    loading,
    error,
  };
};
