import {
  AddProductToCartDocument,
  ClearCartDocument,
  MyCartDocument,
  RemoveCartItemDocument,
  SetCartDeliveryAddressDocument,
  UpdateCartItemQuantityDocument,
  type MyCartQuery,
} from '~/composables/graphql/generated';
import { useGqlClient } from '~/composables/useGqlClient';

type CartParameters = {
  width: number;
  thickness: number;
  color: string;
};

export type ClientCartItem = {
  productId: string;
  productName: string;
  sku: string;
  isCustomizable: boolean;
  quantity: number;
  parameters: CartParameters;
};

function normalizeQuantity(value: number) {
  if (!Number.isFinite(value) || value < 0) {
    return 0;
  }

  return Math.floor(value);
}

function normalizeParameters(value: unknown): CartParameters {
  const source = (value && typeof value === 'object') ? value as Partial<CartParameters> : {};

  return {
    width: Number(source.width ?? 100),
    thickness: Number(source.thickness ?? 50),
    color: typeof source.color === 'string' ? source.color : 'прозрачный',
  };
}

function mapCartItem(item: MyCartQuery['myCart']['items'][number]): ClientCartItem {
  return {
    productId: item.productId,
    productName: item.productName,
    sku: item.sku,
    isCustomizable: item.isCustomizable,
    quantity: Number(item.quantity),
    parameters: normalizeParameters(item.parameters),
  };
}

export function useClientCart() {
  const client = useGqlClient();
  const items = useState<ClientCartItem[]>('client-cart-items', () => []);
  const selectedDeliveryAddressId = useState<string>('client-cart-delivery-address-id', () => '');
  const initialized = useState<boolean>('client-cart-initialized', () => false);
  const loading = useState<boolean>('client-cart-loading', () => false);

  const totalPositions = computed(() => items.value.length);
  const totalItems = computed(() => items.value.reduce((sum, item) => sum + item.quantity, 0));
  const totalVolume = computed(() =>
    items.value.reduce((sum, item) => sum + item.quantity * item.parameters.width * item.parameters.thickness, 0),
  );

  function applyCart(cart: MyCartQuery['myCart']) {
    items.value = cart.items.map(mapCartItem);
    selectedDeliveryAddressId.value = cart.deliveryAddressId ?? '';
    initialized.value = true;
  }

  async function fetchCart(force = false) {
    if (loading.value) {
      return;
    }

    if (initialized.value && !force) {
      return;
    }

    loading.value = true;

    try {
      const { data } = await client.query({
        query: MyCartDocument,
        fetchPolicy: 'network-only',
      });

      if (data?.myCart) {
        applyCart(data.myCart);
      }
    } finally {
      loading.value = false;
    }
  }

  function getQuantity(productId: string) {
    return items.value.find((item) => item.productId === productId)?.quantity ?? 0;
  }

  async function addProduct(product: {
    id: string;
    name: string;
    sku: string;
    isCustomizable: boolean;
  }) {
    const { data } = await client.mutate({
      mutation: AddProductToCartDocument,
      variables: { productId: product.id },
      fetchPolicy: 'no-cache',
    });

    if (data?.addProductToCart) {
      applyCart(data.addProductToCart);
    }
  }

  async function setQuantity(productId: string, quantity: number) {
    const normalizedQuantity = normalizeQuantity(quantity);
    const { data } = await client.mutate({
      mutation: UpdateCartItemQuantityDocument,
      variables: {
        input: {
          productId,
          quantity: normalizedQuantity,
        },
      },
      fetchPolicy: 'no-cache',
    });

    if (data?.updateCartItemQuantity) {
      applyCart(data.updateCartItemQuantity);
    }
  }

  async function incrementQuantity(productId: string) {
    await setQuantity(productId, getQuantity(productId) + 1);
  }

  async function decrementQuantity(productId: string) {
    await setQuantity(productId, getQuantity(productId) - 1);
  }

  async function removeProduct(productId: string) {
    const { data } = await client.mutate({
      mutation: RemoveCartItemDocument,
      variables: { productId },
      fetchPolicy: 'no-cache',
    });

    if (data?.removeCartItem) {
      applyCart(data.removeCartItem);
    }
  }

  async function setDeliveryAddress(addressId: string | null) {
    const { data } = await client.mutate({
      mutation: SetCartDeliveryAddressDocument,
      variables: { addressId: addressId || null },
      fetchPolicy: 'no-cache',
    });

    if (data?.setCartDeliveryAddress) {
      applyCart(data.setCartDeliveryAddress);
    }
  }

  async function clearCart() {
    const { data } = await client.mutate({
      mutation: ClearCartDocument,
      fetchPolicy: 'no-cache',
    });

    if (data?.clearCart) {
      applyCart(data.clearCart);
    }
  }

  if (!initialized.value && !loading.value) {
    void fetchCart();
  }

  return {
    items,
    loading,
    selectedDeliveryAddressId,
    totalPositions,
    totalItems,
    totalVolume,
    fetchCart,
    addProduct,
    setQuantity,
    incrementQuantity,
    decrementQuantity,
    removeProduct,
    clearCart,
    setDeliveryAddress,
    getQuantity,
  };
}
