import { code } from 'dashboard/generated/controlplane';
import { 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;

const serviceCatalogURLRegex =
  '^(?:https://)catalog.xarth.tv/services/([0-9]+)/details$';

export function serviceFullyFilled(service: twirp.IService): boolean {
  const serviceFieldsFilled = !!(
    service.serviceCatalogUrl && service.ldapGroup
  );
  const iamRolesFilled = _.every(service.iamRoles, (iamRole) => {
    return !!(iamRole.arn && iamRole.label);
  });

  return serviceFieldsFilled && iamRolesFilled;
}

export function isDuplicateServiceCatalogID(
  service: twirp.Service,
  existingServiceCatalogIDs: string[],
): boolean {
  const id = serviceCatalogID(service.serviceCatalogUrl);
  return _.includes(existingServiceCatalogIDs, id);
}

export function serviceCatalogIDs(services: twirp.IService[]): string[] {
  if (services === null) {
    return [];
  }
  return _.map(services, (service) => {
    if (_.isNil(service)) {
      return '';
    }
    return serviceCatalogID(service.serviceCatalogUrl as string);
  });
}

export function serviceCatalogID(url: string): string {
  const split = url.split('/');
  return split.length === 6 ? split[4] : '';
}

export function isInvalidServiceCatalogURL(serviceCatalogURL: string): boolean {
  return !RegExp(serviceCatalogURLRegex).test(serviceCatalogURL);
}

export const useServices = () => {
  const [services, setServices] = useState<twirp.IService[]>(
    new Array<twirp.IService>(),
  );
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  const listServicesReq = new twirp.ListServicesReq({});

  const client = createServices(GetControlplaneEndpoint());

  useEffect(() => {
    const fetchData = async () => {
      try {
        const s = await client.list(listServicesReq);
        setServices(s.services);
        setLoading(false);
      } catch (err) {
        setLoading(false);
        setError(err);
      }
    };
    fetchData();
  }, []);

  return { services, loading, error };
};

export const useService = (id: string) => {
  const [service, setService] = useState<twirp.IService>(new twirp.Service());
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);
  const [forbidden, setForbidden] = useState(false);
  const [notFound, setNotFound] = useState(false);
  const [reloadToggle, setReloadToggle] = useState(false);

  const reload = () => setReloadToggle(!reloadToggle);

  const getServiceReq = new twirp.GetServiceReq({
    id,
  });

  const client = createServices(GetControlplaneEndpoint());

  useEffect(() => {
    if (_.isNil(id) || id === '') {
      return;
    }
    const fetchData = async () => {
      try {
        const s = await client.get(getServiceReq);
        setService(s);
        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();
  }, [id, reloadToggle]);

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

export const updateService = async (
  service: twirp.IService,
  success: (s: twirp.IService) => void,
  error: (e: Error) => void,
) => {
  const client = createServices(GetControlplaneEndpoint());
  const req: twirp.IUpdateServiceReq = {
    service,
  };
  try {
    const resp = await client.update(req);
    if (_.isNil(resp.service)) {
      throw new Error('unexpected nil in update response');
    }
    success(resp.service);
  } catch (e) {
    error(e);
  }
};

export const createService = async (
  service: twirp.IService,
  success: (s: twirp.IService) => void,
  error: (e: Error) => void,
) => {
  const client = createServices(GetControlplaneEndpoint());
  const req: twirp.ICreateServiceReq = {
    service,
  };
  try {
    const resp = await client.create(req);
    if (_.isNil(resp.service)) {
      throw new Error('unexpected nil in create response');
    }
    success(resp.service);
  } catch (e) {
    error(e);
  }
};
