import DialogContentText from "@material-ui/core/DialogContentText";
import React from "react";
import { FormattedMessage, useIntl } from "react-intl";

import ActionDialog from "@saleor/components/ActionDialog";
import useNavigator from "@saleor/hooks/useNavigator";
import useNotifier from "@saleor/hooks/useNotifier";
import { commonMessages } from "@saleor/intl";
import useWarehouseSearch from "@saleor/searches/useWarehouseSearch";
import { DEFAULT_INITIAL_SEARCH_DATA } from "@saleor/config";
import {
  useShippingRateCreate,
  useShippingRateUpdate,
  useShippingRateDelete,
  useShippingZoneDelete,
  useShippingZoneUpdate,
  useAssignShippingZoneToWarehouse
} from "@saleor/shipping/mutations";
import createDialogActionHandlers from "@saleor/utils/handlers/dialogActionHandlers";
import ShippingZoneRateDialog from "@saleor/shipping/components/ShippingZoneRateDialog";
import useShop from "@saleor/hooks/useShop";
import ShippingZoneCountriesAssignDialog from "@saleor/shipping/components/ShippingZoneCountriesAssignDialog";
import NotFoundPage from "@saleor/components/NotFoundPage";
import ShippingZoneAddWarehouseDialog from "@saleor/shipping/components/ShippingZoneAddWarehouseDialog";
import { useWarehouseCreate } from "@saleor/warehouses/mutations";
import { getStringOrPlaceholder, findValueInEnum } from "../../../misc";
import {
  ShippingMethodTypeEnum,
  CountryCode
} from "../../../types/globalTypes";
import ShippingZoneDetailsPage, {
  FormData
} from "../../components/ShippingZoneDetailsPage";
import { useShippingZone } from "../../queries";
import {
  shippingZonesListUrl,
  shippingZoneUrl,
  ShippingZoneUrlQueryParams,
  ShippingZoneUrlDialog
} from "../../urls";
import {
  getCreateShippingRateVariables,
  getUpdateShippingRateVariables
} from "./data";

export interface ShippingZoneDetailsProps {
  id: string;
  params: ShippingZoneUrlQueryParams;
}

const ShippingZoneDetails: React.FC<ShippingZoneDetailsProps> = ({
  id,
  params
}) => {
  const navigate = useNavigator();
  const notify = useNotifier();
  const intl = useIntl();
  const shop = useShop();

  const { result: searchWarehousesOpts, loadMore, search } = useWarehouseSearch(
    {
      variables: DEFAULT_INITIAL_SEARCH_DATA
    }
  );

  const [assignToWarehouse] = useAssignShippingZoneToWarehouse({});

  const { data, loading } = useShippingZone({
    displayLoader: true,
    variables: { id }
  });

  const [openModal, closeModal] = createDialogActionHandlers<
    ShippingZoneUrlDialog,
    ShippingZoneUrlQueryParams
  >(navigate, params => shippingZoneUrl(id, params), params);
  const rate = data?.shippingZone?.shippingMethods.find(
    rate => rate.id === params.id
  );

  const [createShippingRate, createShippingRateOpts] = useShippingRateCreate({
    onCompleted: data => {
      if (data.shippingPriceCreate.errors.length === 0) {
        notify({
          text: intl.formatMessage(commonMessages.savedChanges)
        });
        closeModal();
      }
    }
  });

  const [updateShippingRate, updateShippingRateOpts] = useShippingRateUpdate({
    onCompleted: data => {
      if (data.shippingPriceUpdate.errors.length === 0) {
        notify({
          text: intl.formatMessage(commonMessages.savedChanges)
        });
        closeModal();
      }
    }
  });

  const [deleteShippingRate, deleteShippingRateOpts] = useShippingRateDelete({
    onCompleted: data => {
      if (data.shippingPriceDelete.errors.length === 0) {
        notify({
          text: intl.formatMessage(commonMessages.savedChanges)
        });
        closeModal();
      }
    }
  });

  const [deleteShippingZone, deleteShippingZoneOpts] = useShippingZoneDelete({
    onCompleted: data => {
      if (data.shippingZoneDelete.errors.length === 0) {
        notify({
          text: intl.formatMessage(commonMessages.savedChanges)
        });
        navigate(shippingZonesListUrl(), true);
      }
    }
  });

  const [updateShippingZone, updateShippingZoneOpts] = useShippingZoneUpdate({
    onCompleted: data => {
      if (data.shippingZoneUpdate.errors.length === 0) {
        notify({
          text: intl.formatMessage(commonMessages.savedChanges)
        });
        closeModal();
      }
    }
  });

  const [createWarehouse, createWarehouseOpts] = useWarehouseCreate({
    onCompleted: data => {
      if (data.createWarehouse.errors.length === 0) {
        notify({
          text: intl.formatMessage(commonMessages.savedChanges)
        });
        closeModal();
      }
    }
  });

  const handleSubmit = async (data: FormData) => {
    try {
      const updateResult = await updateShippingZone({
        variables: {
          id,
          input: {
            name: data.name
          }
        }
      });
      const updateErrors = updateResult.data.shippingZoneUpdate.errors;

      if (updateErrors.length === 0) {
        const assignResult = await assignToWarehouse({
          variables: {
            shippingZoneId: id,
            warehouseId: data.warehouse
          }
        });
        const assignErrors =
          assignResult.data.assignWarehouseShippingZone.errors;

        if (assignErrors.length === 0) {
          notify({
            text: intl.formatMessage(commonMessages.savedChanges)
          });
        } else {
          throw new Error(
            `Assigning to warehouse failed: ${assignErrors[0].code}`
          );
        }
      } else {
        throw new Error(`Updating failed: ${updateErrors[0].code}`);
      }
    } catch (err) {
      notify({
        text: intl.formatMessage(commonMessages.somethingWentWrong)
      });
    }
  };

  if (data?.shippingZone === null) {
    return <NotFoundPage onBack={() => navigate(shippingZonesListUrl())} />;
  }

  return (
    <>
      <ShippingZoneDetailsPage
        disabled={loading}
        errors={updateShippingZoneOpts.data?.shippingZoneUpdate.errors || []}
        onBack={() => navigate(shippingZonesListUrl())}
        onCountryAdd={() => openModal("assign-country")}
        onCountryRemove={code =>
          openModal("unassign-country", {
            id: code
          })
        }
        onDelete={() => openModal("remove")}
        onPriceRateAdd={() =>
          openModal("add-rate", {
            type: ShippingMethodTypeEnum.PRICE
          })
        }
        onPriceRateEdit={rateId =>
          openModal("edit-rate", {
            id: rateId
          })
        }
        onRateRemove={rateId =>
          openModal("remove-rate", {
            id: rateId
          })
        }
        onSubmit={handleSubmit}
        onWarehouseAdd={() => openModal("add-warehouse")}
        onWeightRateAdd={() =>
          openModal("add-rate", {
            type: ShippingMethodTypeEnum.WEIGHT
          })
        }
        onWeightRateEdit={rateId =>
          openModal("edit-rate", {
            id: rateId
          })
        }
        saveButtonBarState={updateShippingZoneOpts.status}
        shippingZone={data?.shippingZone}
        warehouses={
          searchWarehousesOpts.data?.search.edges.map(edge => edge.node) || []
        }
        hasMore={searchWarehousesOpts.data?.search.pageInfo.hasNextPage}
        loading={searchWarehousesOpts.loading}
        onFetchMore={loadMore}
        onSearchChange={search}
      />
      <ShippingZoneRateDialog
        action="edit"
        confirmButtonState={updateShippingRateOpts.status}
        defaultCurrency={shop?.defaultCurrency}
        disabled={updateShippingRateOpts.loading}
        errors={updateShippingRateOpts.data?.shippingPriceUpdate.errors || []}
        onClose={closeModal}
        onSubmit={data =>
          updateShippingRate({
            variables: getUpdateShippingRateVariables(data, params, id)
          })
        }
        open={params.action === "edit-rate"}
        rate={rate}
        variant={rate?.type}
      />
      <ActionDialog
        confirmButtonState={deleteShippingRateOpts.status}
        onClose={closeModal}
        onConfirm={() =>
          deleteShippingRate({
            variables: {
              id: params.id
            }
          })
        }
        open={params.action === "remove-rate"}
        title={intl.formatMessage({
          defaultMessage: "Delete Shipping Method",
          description: "dialog header"
        })}
        variant="delete"
      >
        <DialogContentText>
          <FormattedMessage
            defaultMessage="Are you sure you want to delete {name}?"
            description="delete shipping method"
            id="shippingZoneDetailsDialogsDeleteShippingMethod"
            values={{
              name: getStringOrPlaceholder(rate?.name)
            }}
          />
        </DialogContentText>
      </ActionDialog>
      <ShippingZoneRateDialog
        action="create"
        confirmButtonState={createShippingRateOpts.status}
        defaultCurrency={shop?.defaultCurrency}
        disabled={createShippingRateOpts.loading}
        errors={createShippingRateOpts.data?.shippingPriceCreate.errors || []}
        onClose={closeModal}
        onSubmit={data =>
          createShippingRate({
            variables: getCreateShippingRateVariables(data, params, id)
          })
        }
        open={params.action === "add-rate"}
        rate={undefined}
        variant={params.type}
      />
      <ActionDialog
        confirmButtonState={deleteShippingZoneOpts.status}
        onClose={closeModal}
        onConfirm={() =>
          deleteShippingZone({
            variables: {
              id
            }
          })
        }
        open={params.action === "remove"}
        title={intl.formatMessage({
          defaultMessage: "Delete Shipping Zone",
          description: "dialog header"
        })}
        variant="delete"
      >
        <DialogContentText>
          <FormattedMessage
            defaultMessage="Are you sure you want to delete {name}?"
            description="delete shipping zone"
            id="shippingZoneDetailsDialogsDeleteShippingZone"
            values={{
              name: (
                <strong>
                  {getStringOrPlaceholder(data?.shippingZone.name)}
                </strong>
              )
            }}
          />
        </DialogContentText>
      </ActionDialog>
      <ShippingZoneCountriesAssignDialog
        confirmButtonState={updateShippingZoneOpts.status}
        countries={shop?.countries || []}
        initial={
          data?.shippingZone?.countries.map(country => country.code) || []
        }
        isDefault={data?.shippingZone?.default}
        onClose={closeModal}
        onConfirm={formData =>
          updateShippingZone({
            variables: {
              id,
              input: {
                countries: formData.countries,
                default: formData.restOfTheWorld
              }
            }
          })
        }
        open={params.action === "assign-country"}
      />
      <ActionDialog
        confirmButtonState={updateShippingZoneOpts.status}
        onClose={closeModal}
        onConfirm={() =>
          updateShippingZone({
            variables: {
              id,
              input: {
                countries: data.shippingZone.countries
                  .filter(country => country.code !== params.id)
                  .map(country => country.code)
              }
            }
          })
        }
        open={params.action === "unassign-country"}
        title={intl.formatMessage({
          defaultMessage: "Delete from Shipping Zone",
          description: "unassign country, dialog header"
        })}
        variant="delete"
      >
        <DialogContentText>
          <FormattedMessage
            defaultMessage="Are you sure you want to delete {countryName} from this shipping zone?"
            description="unassign country"
            values={{
              countryName: (
                <strong>
                  {getStringOrPlaceholder(
                    data?.shippingZone?.countries.find(
                      country => country.code === params.id
                    )?.country
                  )}
                </strong>
              )
            }}
          />
        </DialogContentText>
      </ActionDialog>
      <ShippingZoneAddWarehouseDialog
        countries={shop?.countries || []}
        disabled={createWarehouseOpts.loading}
        open={params.action === "add-warehouse"}
        confirmButtonState={createWarehouseOpts.status}
        errors={createWarehouseOpts.data?.createWarehouse.errors || []}
        onClose={closeModal}
        onSubmit={data =>
          createWarehouse({
            variables: {
              input: {
                address: {
                  city: data.city,
                  cityArea: data.cityArea,
                  country: findValueInEnum(data.country, CountryCode),
                  countryArea: data.countryArea,
                  phone: data.phone,
                  postalCode: data.postalCode,
                  streetAddress1: data.streetAddress1,
                  streetAddress2: data.streetAddress2
                },
                companyName: data.companyName,
                name: data.name
              }
            }
          })
        }
      />
    </>
  );
};
export default ShippingZoneDetails;
