import { Errored } from 'dashboard/core/components/errored';
import { Forbidden } from 'dashboard/core/components/forbidden';
import { StyledLoadingSpinner } from 'dashboard/core/components/loading-spinner';
import { BannerContext } from 'dashboard/core/contexts/banners';
import { ModalContext } from 'dashboard/core/contexts/modals';
import { useProductionEventStreams } from 'dashboard/core/utils/event-stream';
import { useService } from 'dashboard/core/utils/service';
import { useSubscriptions } from 'dashboard/core/utils/subscriptions';
import { useTarget } from 'dashboard/core/utils/target';
import { code } from 'dashboard/generated/controlplane';
import { AddSubscriptionModal } from 'dashboard/pages/subscriptions/components/add-subscription-modal';
import { RemoveSubscriptionModal } from 'dashboard/pages/subscriptions/components/remove-subscription-modal';
import { SubscriptionRow } from 'dashboard/pages/subscriptions/components/subscription-row';
import * as _ from 'lodash';
import * as React from 'react';
import { useContext, useEffect, useState } from 'react';
import { useParams } from 'react-router-dom';
import {
  AlertBannerType,
  Background,
  Color,
  Column,
  CoreLink,
  CoreText,
  Display,
  FlexDirection,
  FontSize,
  Grid,
  JustifyContent,
  Layout,
  SearchInput,
  StyledLayout,
  TextType,
} from 'twitch-core-ui';

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

export const SubscriptionsPage = () => {
  const { serviceId, targetId } = useParams();
  const {
    service,
    target,
    subscriptions: fetchedSubscriptions,
    eventStreams,
    loading,
    error,
    forbidden,
  } = useSubscriptionData(serviceId, targetId);

  const [subscriptions, setSubscriptions] = useState(fetchedSubscriptions);
  useEffect(() => {
    setSubscriptions(fetchedSubscriptions);
  }, [fetchedSubscriptions]);

  const { showModal } = useContext(ModalContext);
  const { showBanner } = useContext(BannerContext);

  const [searchInput, setSearchInput] = useState('');
  const handleSearchChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setSearchInput(e.target.value);
  };

  const handleSubscribe = (eventType: string) => {
    return (environment: string) => {
      return () => {
        showModal({
          component: AddSubscriptionModal,
          props: {
            targetId,
            existingSubscriptions: subscriptions,
            eventType,
            environment,
            onCreateSuccess: (s: twirp.ISubscription) => {
              setSubscriptions([...subscriptions, s]);
              showBanner({
                id: 'subscription-created',
                status: 'Success',
                message: `Subscribed to ${eventType}'s ${environment} events`,
                type: AlertBannerType.Success,
              });
            },
          },
        });
      };
    };
  };

  const handleUnsubscribe = (eventType: string) => {
    return (environment: string) => {
      return () => {
        showModal({
          component: RemoveSubscriptionModal,
          props: {
            targetId,
            eventType,
            environment,
            onDeleteSuccess: (deleted: twirp.ISubscription) => {
              setSubscriptions(
                _.filter(
                  subscriptions,
                  (s) =>
                    !(
                      s.eventType === deleted.eventType &&
                      s.environment === deleted.environment
                    ),
                ),
              );
              showBanner({
                id: 'subscription-created',
                status: 'Success',
                message: `Unsubscribed from ${eventType}'s ${environment} events`,
                type: AlertBannerType.Success,
              });
            },
          },
        });
      };
    };
  };

  if (forbidden) {
    return <Forbidden />;
  } else if (error) {
    return <Errored text={error.message} />;
  } else if (loading) {
    return <StyledLoadingSpinner />;
  }

  const eventsWithSubscriptions = _.filter(
    eventStreams,
    (eventStream) =>
      !_.isNil(_.find(subscriptions, { eventType: eventStream.eventType })),
  );

  const eventsWithoutSubscriptions = _.filter(eventStreams, (eventStream) =>
    _.isNil(_.find(subscriptions, { eventType: eventStream.eventType })),
  );

  const filteredEventsWithoutSubscriptions = _.filter(
    eventsWithoutSubscriptions,
    (es) =>
      _.includes(_.toLower(es.eventType as string), _.toLower(searchInput)),
  );

  const isSubscribed = (eventType: string, environment: string): boolean => {
    return !_.isNil(
      _.find(subscriptions, {
        eventType: eventType as string,
        environment: environment as string,
      }),
    );
  };

  return (
    <Grid>
      <Column cols={12}>
        <StyledLayout padding={0.5}>
          <CoreText type={TextType.H3}>Subscriptions</CoreText>
        </StyledLayout>
      </Column>

      <Column cols={12}>
        <StyledLayout padding={1}>
          <Grid>
            <Column cols={2}>
              <CoreText type={TextType.H6} bold>
                Service:
              </CoreText>
            </Column>
            <Column cols={10}>
              <CoreText type={TextType.H6}>
                <CoreLink linkTo={'/services/' + serviceId}>
                  {service.name}
                </CoreLink>
              </CoreText>
            </Column>
            <Column cols={2}>
              <CoreText type={TextType.H6} bold>
                Target Name:
              </CoreText>
            </Column>
            <Column cols={10}>
              <CoreText type={TextType.H6}>{target.name as string}</CoreText>
            </Column>
            <Column cols={2}>
              <CoreText type={TextType.H6} bold>
                Target Queue URL:
              </CoreText>
            </Column>
            <Column cols={10}>
              <CoreText type={TextType.H6}>
                {!_.isNil(target.sqs) ? target.sqs.queueUrl : ''}
              </CoreText>
            </Column>
          </Grid>
        </StyledLayout>
      </Column>

      <Column cols={12}>
        <Layout padding={1}>
          <CoreText fontSize={FontSize.Size4}>
            Events with Active Subscriptions
          </CoreText>
        </Layout>
      </Column>

      <Column cols={12}>
        {_.isEmpty(eventsWithSubscriptions) ? (
          <Layout
            padding={1}
            justifyContent={JustifyContent.Center}
            display={Display.Flex}
            fullWidth
          >
            <CoreText color={Color.Alt2} fontSize={FontSize.Size6}>
              No subscriptions, yet! Browse the catalog below to subscribe to
              events.
            </CoreText>
          </Layout>
        ) : (
          <StyledLayout
            margin={1}
            elevation={1}
            className="active-subscriptions"
          >
            <StyledLayout
              display={Display.Flex}
              flexDirection={FlexDirection.Row}
              background={Background.Alt}
            >
              <Layout
                padding={1}
                flexGrow={1}
                display={Display.Flex}
                justifyContent={JustifyContent.Center}
              >
                <CoreText bold fontSize={FontSize.Size6}>
                  Event Type
                </CoreText>
              </Layout>
              <Layout
                padding={1}
                flexGrow={1}
                display={Display.Flex}
                justifyContent={JustifyContent.Center}
              >
                <CoreText bold fontSize={FontSize.Size6}>
                  Environments
                </CoreText>
              </Layout>
            </StyledLayout>
            {_.map(eventsWithSubscriptions, (es) => (
              <SubscriptionRow
                eventStream={es}
                productionEnabled={isSubscribed(
                  es.eventType as string,
                  'production',
                )}
                stagingEnabled={isSubscribed(es.eventType as string, 'staging')}
                developmentEnabled={isSubscribed(
                  es.eventType as string,
                  'development',
                )}
                onSubscribe={handleSubscribe(es.eventType as string)}
                onUnsubscribe={handleUnsubscribe(es.eventType as string)}
              />
            ))}
          </StyledLayout>
        )}
      </Column>

      <Column cols={12}>
        <Layout padding={1}>
          <CoreText fontSize={FontSize.Size4}>Other Events</CoreText>
        </Layout>
      </Column>

      <Column cols={3}>
        <Layout padding={{ x: 1 }}>
          <SearchInput
            value={searchInput}
            onChange={handleSearchChange}
            placeholder="Search for events here..."
          />
        </Layout>
      </Column>

      <Column cols={9} />

      <Column cols={12}>
        <StyledLayout
          margin={1}
          elevation={1}
          className="no-active-subscriptions"
        >
          <StyledLayout
            display={Display.Flex}
            flexDirection={FlexDirection.Row}
            background={Background.Alt}
          >
            <Layout
              padding={1}
              flexGrow={1}
              display={Display.Flex}
              justifyContent={JustifyContent.Center}
            >
              <CoreText bold fontSize={FontSize.Size6}>
                Event Type
              </CoreText>
            </Layout>
            <Layout
              padding={1}
              flexGrow={1}
              display={Display.Flex}
              justifyContent={JustifyContent.Center}
            >
              <CoreText bold fontSize={FontSize.Size6}>
                Environments
              </CoreText>
            </Layout>
          </StyledLayout>
          {_.map(filteredEventsWithoutSubscriptions, (es) => (
            <SubscriptionRow
              eventStream={es}
              // We know these are all false because the entries are in the bottom table
              productionEnabled={false}
              stagingEnabled={false}
              developmentEnabled={false}
              onSubscribe={handleSubscribe(es.eventType as string)}
              onUnsubscribe={handleUnsubscribe(es.eventType as string)} // not needed in theory
            />
          ))}
        </StyledLayout>
      </Column>
    </Grid>
  );
};

const useSubscriptionData = (serviceId: string, targetId: string) => {
  const {
    service,
    loading: serviceLoading,
    error: serviceError,
    forbidden: serviceForbidden,
  } = useService(serviceId);

  const {
    productionEventStreams: eventStreams,
    loading: eventStreamsLoading,
    error: eventStreamsError,
  } = useProductionEventStreams();

  const {
    target,
    loading: targetLoading,
    error: targetError,
    forbidden: targetForbidden,
  } = useTarget(serviceId, targetId);

  const {
    subscriptions,
    loading: subscriptionsLoading,
    error: subscriptionsError,
    forbidden: subscriptionsForbidden,
  } = useSubscriptions(targetId);

  const loading =
    serviceLoading ||
    eventStreamsLoading ||
    targetLoading ||
    subscriptionsLoading;
  const error =
    serviceError || targetError || eventStreamsError || subscriptionsError;
  const forbidden =
    serviceForbidden || targetForbidden || subscriptionsForbidden;

  return {
    service,
    target,
    subscriptions,
    eventStreams,
    loading,
    error,
    forbidden,
  };
};
