import { useMemo, useState } from 'react';
import { PaymentMethod } from '@stripe/stripe-js';
import { useQuery } from '@apollo/client';
import { CHECKOUT_PROPS } from 'components/billing/schema/queries';
import { CheckoutPropsQuery, SubscriptionPagePropsQuery } from 'types/graphql';

import { Box, Button, Heading, Spinner } from 'components/common';
import { StripeElements } from 'components/stripe/StripeElements';
import { ErrorBoundary } from 'components/ErrorBoundary';
import {
  BillingInformation,
  BillingInformationProps,
  isEmpty,
} from 'components/billing/BillingInformation';
import { SubscriptionPlan } from 'components/billing/SubscriptionPlans';
import { CheckoutTotal } from 'components/billing/CheckoutTotal';
import { CheckoutSuccess } from 'components/billing/CheckoutSuccess';
import {
  CheckoutPaymentMethod,
  CheckoutPaymentMethodProps,
} from 'components/billing/CheckoutPaymentMethod';

import { useRequestReducer } from 'components/billing/useRequestReducer';
import { useCreateSubscription } from 'components/billing/useCreateSubscription';
import { catchError } from 'utils/catchError';

function removeTypename(res: any) {
  const { __typename, ...rest } = res;
  return rest;
}

interface SubscriptionFormProps {
  plan: SubscriptionPagePropsQuery['packages'][0];
  billingCycle: string;
  paymentMethod: CheckoutPaymentMethodProps['method'] | null;
  billingDetails: BillingInformationProps['data'];
  onCancel?: () => void;
}

export const SubscriptionForm = ({
  plan,
  billingCycle,
  paymentMethod,
  billingDetails,
  onCancel,
}: SubscriptionFormProps) => {
  // hotfix to remove __typename
  billingDetails = useMemo(
    () => removeTypename(billingDetails),
    [billingDetails]
  );
  const [method, setMethod] = useState(paymentMethod);

  const [state, dispatch] = useRequestReducer<{ status: string }>();
  const createSubscription = useCreateSubscription();

  const subscribe = async () => {
    try {
      dispatch('REQUEST');

      const result = await createSubscription(
        plan.id,
        billingCycle,
        method!.id
      );

      if (result.status === 'succeeded') {
        dispatch('SUCCESS', 'Success', { status: 'succeeded' });
      } else if (result.error) {
        dispatch('FAILURE', result.error.message || 'Something went wrong', {
          status: result.status,
        });
      } else {
        console.log('else', result.error, result.status);
      }
    } catch (err) {
      console.log('catch', err);
      dispatch('FAILURE', catchError(err).message || 'Something went wrong');
    }
  };

  const pricing = billingCycle === 'annual' ? plan.prices[0] : plan.prices[1];
  const billingDetailsRequired = isEmpty(billingDetails);
  const ready =
    plan?.id && method?.id && billingCycle && !billingDetailsRequired;

  const paymentSuccess = !state.error && state.payload?.status === 'succeeded';
  const paymentMethodError =
    state.error && state.payload?.status === 'requires_payment_method';

  const handleNewPaymentMethod = (pm: PaymentMethod) => {
    setMethod({
      id: pm.id,
      created: pm.created + '',
      brand: pm.card?.brand || '',
      country: pm.card?.country || '',
      expMonth: pm.card?.exp_month + '',
      expYear: pm.card?.exp_year + '',
      last4: pm.card?.last4 || '',
    });
    dispatch('RESET');
  };

  if (paymentSuccess) {
    return (
      <ErrorBoundary>
        <CheckoutSuccess />
      </ErrorBoundary>
    );
  }

  return (
    <Box position="relative" spacingY={10} style={{ paddingBottom: 100 }}>
      <ErrorBoundary>
        <Box spacingY={5}>
          <Heading>Confirm plan selection</Heading>
          <div
            className="padding4 rounded4"
            style={{ border: '1px solid var(--borderColor)' }}
          >
            <SubscriptionPlan
              name={plan?.name}
              description={plan?.description}
              price={pricing?.price}
              campaignPrice={pricing?.campaignPrice}
              campaignCode={plan?.campaignCode}
              quota={plan?.quotas}
            />
          </div>
        </Box>
      </ErrorBoundary>

      <ErrorBoundary>
        <BillingInformation
          data={billingDetails}
          showForm={billingDetailsRequired}
          disabled={billingDetailsRequired || state.loading}
        />
      </ErrorBoundary>

      <ErrorBoundary>
        <CheckoutPaymentMethod
          method={method}
          onChange={handleNewPaymentMethod}
          billingDetails={billingDetails}
          error={paymentMethodError ? state.message! : undefined}
          disabled={state.loading}
        />
      </ErrorBoundary>

      {state.error && !paymentMethodError && (
        <div className="fz14 mark--red padding4 rounded4">{state.message}</div>
      )}

      <ErrorBoundary>
        <CheckoutTotal
          billingCycle={billingCycle}
          price={pricing?.price}
          campaignPrice={pricing?.campaignPrice}
          campaignDescription={plan?.campaignDescription}
        />
      </ErrorBoundary>

      <div className="flex justifyBetween">
        {onCancel && (
          <Button
            onClick={onCancel}
            size="l"
            color="gray"
            disabled={state.loading || !onCancel}
          >
            Cancel
          </Button>
        )}
        <Button onClick={subscribe} size="l" disabled={state.loading || !ready}>
          Subscribe
        </Button>
      </div>

      <Spinner style={{ zIndex: 100 }} show={state.loading} />
    </Box>
  );
};

interface CheckoutScreenProps {
  plan: SubscriptionPagePropsQuery['packages'][0];
  billingCycle: string;
  onCancel?: () => void;
}

export const CheckoutScreen = ({
  plan,
  billingCycle,
  onCancel,
}: CheckoutScreenProps) => {
  const { loading, data, error } = useQuery<CheckoutPropsQuery>(
    CHECKOUT_PROPS,
    {
      fetchPolicy: 'cache-and-network',
      nextFetchPolicy: 'cache-first',
    }
  );

  if (loading && data === undefined) return <Spinner show />;
  if (error || !data) return <div>Something went wrong</div>;

  return (
    <StripeElements>
      <SubscriptionForm
        plan={plan}
        billingCycle={billingCycle}
        billingDetails={data.billingDetails}
        paymentMethod={data.paymentMethod}
        onCancel={onCancel}
      />
    </StripeElements>
  );
};
