fetchProduct is keeping getting called forever... reduce all the unnessearcy rerenders and fetching... each product should fetched only one!! one not more!
"use client";
import React, { useState, useEffect, useCallback, useMemo } from "react";
import DefaultLayout from "@/components/Layouts/DefaultLayout";
import {
  Trash2,
  Search,
  ChevronDown,
  ChevronUp,
  ChevronLeft,
  ChevronRight,
} from "lucide-react";
import { toast, ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.css";
import Image from "next/image";
import { getToken } from "@/app/auth";

const API_BASE_URL = "https://api2.naayiq.com";

interface OrderItem {
  price: string;
  id: number;
  cart_id: number;
  product_id: number;
  size_id?: number | null;
  color_id?: number | null;
  quantity: number;
}

interface CartWithItems {
  id: number;
  user_id?: number | null;
  status: string;
  notes?: string;
  full_name?: string;
  governorate?: string;
  city?: string;
  address?: string;
  closest_point?: string;
  phone_number?: string;
  type?: string;
  created_at: string;
  coupon?: {
    id: number;
    code: string;
    name: string;
    description: string;
    discount_amount: number;
    discount_percentage: number;
    start_date: string;
    end_date: string;
    usage_limit: number;
    min_purchase_amount: number;
    max_discount_amount: number;
    is_active: boolean;
  } | null;
  updated_at: string;
  items: OrderItem[];
}

interface Product {
  id: number;
  name: string;
  description?: string;
  brand_id?: number;
  has_color: boolean;
  has_size: boolean;
  product_type: string;
  price: string;
  qty: number | null;
  created_at: string;
  updated_at: string;
  categories: Array<{ id: number; name: string }>;
  colors: Array<{ id: number; name: string }>;
  sizes: Array<{
    id: number;
    name: string;
    price: string;
    qty: number;
    color_id: number | null;
    images: string[];
  }>;
  images: Array<{
    id: number;
    url: string;
    color_id: number | null;
    size_id: number | null;
  }>;
}

interface Coupon {
  id: number;
  code: string;
  discount_type: string;
  discount_value: number;
}

const formatPrice = (price: number) => {
  return `${price.toLocaleString()} IQD`;
};

const getStatusColor = (status: string) => {
  switch (status) {
    case "Pending":
      return "bg-orange-100 text-orange-800";
    case "On Way":
      return "bg-indigo-100 text-indigo-800";
    case "Ordered":
      return "bg-emerald-100 text-emerald-800";
    case "Rejected":
      return "bg-red text-white";
    default:
      return "bg-gray-100 text-gray-800";
  }
};

const OrdersPage: React.FC = () => {
  const [orders, setOrders] = useState<CartWithItems[]>([]);
  const [products, setProducts] = useState<{ [key: number]: Product }>({});
  const [coupons, setCoupons] = useState<{ [key: number]: Coupon }>({});
  const [error, setError] = useState<string | null>(null);
  const [searchTerm, setSearchTerm] = useState("");
  const [first, setFirst] = useState(true);
  const [currentPage, setCurrentPage] = useState(1);
  const [ordersPerPage] = useState(10);
  const [statusFilter, setStatusFilter] = useState<string>("all");
  const [sortOrder, setSortOrder] = useState<"asc" | "desc">("desc");
  const filteredAndSortedOrders = useMemo(() => {
    return orders
      .filter((order) => {
        const matchesSearch =
          order.full_name?.toLowerCase().includes(searchTerm.toLowerCase()) ||
          order.id.toString().includes(searchTerm);
        const matchesStatus =
          statusFilter === "all" || order.status === statusFilter;
        return matchesSearch && matchesStatus;
      })
      .sort((a, b) => {
        const dateA = new Date(a.created_at).getTime();
        const dateB = new Date(b.created_at).getTime();
        return sortOrder === "asc" ? dateA - dateB : dateB - dateA;
      });
  }, [orders, searchTerm, statusFilter, sortOrder]);

  const totalPages = Math.ceil(filteredAndSortedOrders.length / ordersPerPage);

  const currentOrders = useMemo(() => {
    const indexOfLastOrder = currentPage * ordersPerPage;
    const indexOfFirstOrder = indexOfLastOrder - ordersPerPage;
    return filteredAndSortedOrders.slice(indexOfFirstOrder, indexOfLastOrder);
  }, [filteredAndSortedOrders, currentPage, ordersPerPage]);
  const paginate = (pageNumber: number) => {
    setCurrentPage(Math.max(1, Math.min(pageNumber, totalPages)));
  };
  useEffect(() => {
    window.scrollTo(0, 0);
  }, [currentPage]);
  const fetchOrders = useCallback(async () => {
    try {
      const token = getToken(); // Retrieve the token from localStorage

      if (!token) {
        toast.error("You must be logged in.");
        return;
      }
      const response = await fetch(`${API_BASE_URL}/cart`, {
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${token}`, // Include the token here
        },
      });
      if (!response.ok) throw new Error("Failed to fetch orders");
      const data = await response.json();
      setOrders((prevOrders) => {
        const newOrders = data.filter(
          (order: CartWithItems) =>
            !prevOrders.some((prevOrder) => prevOrder.id === order.id),
        );
        if (newOrders.length > 0 && !first) {
          toast.success(`Received ${newOrders.length} new order(s)!`);
        }
        setFirst(false);
        return data;
      });
    } catch (error) {
      console.error("Error fetching orders:", error);
      setError("Failed to fetch orders. Please try again.");
    }
  }, [first]);

  const fetchProduct = useCallback(
    async (productId: number): Promise<{ product: Product } | null> => {
      if (products[productId]) {
        return { product: products[productId] };
      }
      try {
        const token = getToken();
        if (!token) {
          toast.error("You must be logged in.");
          return null;
        }
        const response = await fetch(`${API_BASE_URL}/products/${productId}`, {
          headers: {
            "Content-Type": "application/json",
            Authorization: `Bearer ${token}`,
          },
        });
        if (!response.ok) throw new Error("Failed to fetch product");
        const data = await response.json();
        setProducts((prev) => ({ ...prev, [productId]: data.product }));
        return data;
      } catch (error) {
        console.error(`Error fetching product ${productId}:`, error);
        return null;
      }
    },
    [products],
  );

  const fetchCoupon = useCallback(
    async (couponId: number): Promise<Coupon | null> => {
      if (coupons[couponId]) {
        return coupons[couponId];
      }
      try {
        const token = getToken(); // Retrieve the token from localStorage

        if (!token) {
          toast.error("You must be logged in.");
          return null;
        }
        const response = await fetch(`${API_BASE_URL}/coupons/${couponId}`, {
          headers: {
            "Content-Type": "application/json",
            Authorization: `Bearer ${token}`, // Include the token here
          },
        });
        if (!response.ok) throw new Error("Failed to fetch coupon");
        const data = await response.json();
        setCoupons((prev) => ({ ...prev, [couponId]: data }));
        return data;
      } catch (error) {
        console.error(`Error fetching coupon ${couponId}:`, error);
        return null;
      }
    },
    [coupons],
  );

  useEffect(() => {
    fetchOrders();
    const intervalId = setInterval(fetchOrders, 10000); // Poll every 10 seconds
    return () => clearInterval(intervalId);
  }, [fetchOrders]);

  const handleUpdateStatus = async (orderId: number, newStatus: string) => {
    const oldStatus =
      orders.find((order) => order.id === orderId)?.status || "";

    // Optimistically update the UI
    setOrders((prevOrders) =>
      prevOrders.map((order) =>
        order.id === orderId ? { ...order, status: newStatus } : order,
      ),
    );

    try {
      const token = getToken();
      if (!token) {
        toast.error("You must be logged in.");
        throw new Error("Not logged in");
      }

      const response = await fetch(`${API_BASE_URL}/cart/carts/${orderId}`, {
        method: "PUT",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${token}`,
        },
        body: JSON.stringify({ status: newStatus }),
      });

      const result = await response.json();
      if (!response.ok) {
        throw new Error(result.message);
      }

      toast.success(result.message);
    } catch (error) {
      console.error("Error updating order status:", error);
      // Revert the status if the update failed
      setOrders((prevOrders) =>
        prevOrders.map((order) =>
          order.id === orderId ? { ...order, status: oldStatus } : order,
        ),
      );
      toast.error("Failed to update order status. Please try again.");
    }
  };
  const handleDeleteOrder = async (orderId: number) => {
    const isConfirmed = window.confirm(
      "Are you sure you want to delete this order?",
    );

    if (isConfirmed) {
      try {
        const token = getToken(); // Retrieve the token from localStorage
        if (!token) {
          toast.error("You must be logged in .");
          return;
        }
        const response = await fetch(`${API_BASE_URL}/cart/carts/${orderId}`, {
          method: "DELETE",
          headers: {
            "Content-Type": "application/json",
            Authorization: `Bearer ${token}`, // Include the token here
          },
        });
        const result = await response.json();
        if (!response.ok) {
          toast.error(result.message);
          throw new Error(result.message);
        } else {
          toast.success(result.message);
        }
        await fetchOrders();
      } catch (error) {
        console.error("Error deleting order:", error);
        setError("Failed to delete order. Please try again.");
      }
    }
  };

  const filteredOrders = useMemo(() => {
    return orders.filter(
      (order) =>
        order.full_name?.toLowerCase().includes(searchTerm.toLowerCase()) ||
        order.id.toString().includes(searchTerm),
    );
  }, [orders, searchTerm]);

  return (
    <DefaultLayout>
      <div className="container mx-auto p-6">
        <h1 className="mb-6 text-3xl font-bold text-gray-800">Orders</h1>
        <div className="mb-4 flex items-center justify-between">
          <div className="flex items-center space-x-4">
            <div className="flex items-center">
              <Search size={20} className="mr-2 text-gray-400" />
              <input
                type="text"
                placeholder="Search orders..."
                value={searchTerm}
                onChange={(e) => setSearchTerm(e.target.value)}
                className="rounded-md border border-gray-300 px-4 py-2"
              />
            </div>
            <select
              value={statusFilter}
              onChange={(e) => setStatusFilter(e.target.value)}
              className="rounded-md border border-gray-300 px-4 py-2"
            >
              <option value="all">All Statuses</option>
              <option value="Pending">Pending</option>
              <option value="On Way">On Way</option>
              <option value="Ordered">Ordered</option>
              <option value="Rejected">Rejected</option>
            </select>
            <select
              value={sortOrder}
              onChange={(e) => setSortOrder(e.target.value as "asc" | "desc")}
              className="rounded-md border border-gray-300 px-4 py-2"
            >
              <option value="desc">Newest First</option>
              <option value="asc">Oldest First</option>
            </select>
          </div>
        </div>

        <div className="space-y-6">
          {currentOrders.map((order) => (
            <OrderDetails
              key={order.id}
              order={order}
              onUpdateStatus={handleUpdateStatus}
              onDeleteOrder={handleDeleteOrder}
              fetchProduct={fetchProduct}
              fetchCoupon={fetchCoupon}
              products={products}
            />
          ))}
        </div>

        <div className="mt-4 flex items-center justify-center space-x-2">
          <button
            onClick={() => paginate(currentPage - 1)}
            disabled={currentPage === 1}
            className="rounded bg-gray-200 p-2 disabled:opacity-50"
          >
            <ChevronLeft size={20} />
          </button>
          <span>
            Page {currentPage} of {totalPages}
          </span>
          <button
            onClick={() => paginate(currentPage + 1)}
            disabled={currentPage === totalPages}
            className="rounded bg-gray-200 p-2 disabled:opacity-50"
          >
            <ChevronRight size={20} />
          </button>
        </div>
        <ToastContainer />
      </div>
    </DefaultLayout>
  );
};
interface OrderDetails {
  totalPrice: number;
  discountedPrice: number;
  deliveryCost: number;
}

interface OrderDetailsProps {
  order: CartWithItems;
  onUpdateStatus: (orderId: number, newStatus: string) => void;
  onDeleteOrder: (orderId: number) => void;
  fetchProduct: (productId: number) => Promise<{ product: Product } | null>;
  fetchCoupon?: (couponId: number) => Promise<Coupon | null>;
  products: { [key: number]: Product };
}
const OrderDetails: React.FC<OrderDetailsProps> = React.memo(
  ({
    order,
    onUpdateStatus,
    onDeleteOrder,
    fetchProduct,
    fetchCoupon,
    products,
  }) => {
    const [isExpanded, setIsExpanded] = useState(false);
    const [orderDetails, setOrderDetails] = useState<OrderDetails>({
      totalPrice: 0,
      discountedPrice: 0,
      deliveryCost: 0,
    });

    useEffect(() => {
      const calculateOrderDetails = async () => {
        let total = 0;
        for (const item of order.items) {
          let product = products[item.product_id];
          if (!product) {
            const productData = await fetchProduct(item.product_id);
            if (productData && productData.product) {
              product = productData.product;
            }
          }
          if (product) {
            const size = product.sizes.find((s) => s.id === item.size_id);
            const itemPrice = size
              ? parseFloat(size.price)
              : parseFloat(product.price);
            total += itemPrice * item.quantity;
          }
        }

        let discountedPrice = total;
        if (order.coupon) {
          let discount = 0;
          if (order.coupon.discount_percentage) {
            discount = total * (order.coupon.discount_percentage / 100);
          } else if (order.coupon.discount_amount) {
            discount = order.coupon.discount_amount;
          }
          if (order.coupon.max_discount_amount) {
            discount = Math.min(discount, order.coupon.max_discount_amount);
          }
          discountedPrice = total - discount;
        }

        const deliveryCost =
          total >= 100000 || order.governorate?.toLowerCase() === "karbala"
            ? 0
            : 5000;

        setOrderDetails({ totalPrice: total, discountedPrice, deliveryCost });
      };

      calculateOrderDetails();
    }, [order, fetchProduct, products]);

    const finalTotal = orderDetails.discountedPrice + orderDetails.deliveryCost;

    const handleStatusChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
      const newStatus = e.target.value;
      onUpdateStatus(order.id, newStatus);
    };

    const getStatusColor = (status: string) => {
      switch (status) {
        case "Pending":
          return "bg-orange-100 text-orange-800";
        case "On Way":
          return "bg-indigo-100 text-indigo-800";
        case "Ordered":
          return "bg-emerald-100 text-emerald-800";
        case "Rejected":
          return "bg-red text-white";
        default:
          return "bg-gray-100 text-gray-800";
      }
    };

    const formatPrice = (price: number) => {
      return `${price.toLocaleString()} IQD`;
    };

    return (
      <div className="rounded-lg bg-white p-6 shadow-md">
        <div className="mb-4 flex items-center justify-between">
          <h2 className="text-xl font-semibold">Order #{order.id}</h2>
          <div className="flex space-x-2">
            <select
              value={order.status}
              onChange={handleStatusChange}
              className={`rounded px-2 py-1 ${getStatusColor(order.status)}`}
            >
              <option value="Pending">Pending</option>
              <option value="On Way">On Way</option>
              <option value="Ordered">Ordered</option>
              <option value="Rejected">Rejected</option>
            </select>
            <button
              onClick={() => onDeleteOrder(order.id)}
              className="rounded bg-red p-1 text-white hover:bg-danger"
            >
              <Trash2 size={16} />
            </button>
          </div>
        </div>
        <div className="grid grid-cols-2 gap-4">
          <div>
            <p>
              <strong>Customer Name:</strong> {order.full_name || "N/A"}
            </p>
            <p>
              <strong>Status:</strong>{" "}
              <span
                className={`rounded px-2 py-1 ${getStatusColor(order.status)}`}
              >
                {order.status}
              </span>
            </p>
            <p>
              <strong>Created At:</strong>{" "}
              {new Date(order.created_at).toLocaleString()}
            </p>
          </div>
          <div>
            <p>
              <strong>Address:</strong> {order.address || "N/A"}
            </p>
            <p>
              <strong>Closest Point:</strong> {order?.closest_point || "N/A"}
            </p>
            <p>
              <strong>Phone Number:</strong> {order.phone_number || "N/A"}
            </p>
            <p>
              <strong>Governorate:</strong> {order.governorate || "N/A"}
            </p>
          </div>
        </div>
        <div className="mt-4">
          <h3 className="text-lg font-semibold">Notes:</h3>
          <p className="mt-2 text-gray-600">
            {order.notes || "No notes provided"}
          </p>
        </div>
        <div className="mt-4">
          <button
            onClick={() => setIsExpanded(!isExpanded)}
            className="flex items-center text-blue-500 hover:text-blue-700"
          >
            <h3 className="mr-2 text-lg font-semibold">Items:</h3>
            {isExpanded ? <ChevronUp size={20} /> : <ChevronDown size={20} />}
          </button>
          {isExpanded && (
            <ul className="mt-2 space-y-4">
              {order.items.map((item) => (
                <OrderItem
                  key={item.id}
                  item={item}
                  fetchProduct={fetchProduct}
                  product={products[item.product_id]}
                />
              ))}
            </ul>
          )}
        </div>
        <div className="mt-4 text-right">
          <p className="text-lg font-semibold">
            Total Price: {formatPrice(orderDetails.totalPrice)}
          </p>
          {order.coupon && (
            <>
              <p className="text-sm text-gray-600">
                Coupon Applied: {order.coupon.code} ({order.coupon.name})
              </p>
              <p className="text-sm text-gray-600">
                Discount:{" "}
                {order.coupon.discount_percentage
                  ? `${order.coupon.discount_percentage}%`
                  : formatPrice(order.coupon.discount_amount)}
                {order.coupon.max_discount_amount &&
                  ` (Max: ${formatPrice(order.coupon.max_discount_amount)})`}
              </p>
              <p className="text-lg font-semibold text-green-600">
                Discounted Price: {formatPrice(orderDetails.discountedPrice)}
              </p>
            </>
          )}
          <p className="text-md mt-2 italic text-gray-600">
            Delivery Cost:{" "}
            {orderDetails.deliveryCost === 0
              ? "Free"
              : formatPrice(orderDetails.deliveryCost)}
          </p>
          <p className="mt-2 text-xl font-bold">
            Final Total: {formatPrice(finalTotal)}
          </p>
        </div>
      </div>
    );
  },
);

const OrderItem: React.FC<{
  item: OrderItem;
  fetchProduct: (productId: number) => Promise<{ product: Product } | null>;
  product?: Product;
}> = React.memo(({ item, fetchProduct, product: initialProduct }) => {
  const [product, setProduct] = useState<Product | null>(
    initialProduct || null,
  );
  const [isLoading, setIsLoading] = useState(!initialProduct);

  useEffect(() => {
    if (!product) {
      setIsLoading(true);
      fetchProduct(item.product_id).then((data) => {
        if (data && data.product) {
          setProduct(data.product);
        }
        setIsLoading(false);
      });
    }
  }, [item.product_id, fetchProduct, product]);

  if (isLoading) {
    return <li className="h-20 animate-pulse rounded bg-gray-200"></li>;
  }

  if (!product) {
    return <li className="text-red-500">Error loading product</li>;
  }

  const size = product.sizes.find((s) => s.id === item.size_id);
  const color = product.colors.find((c) => c.id === item.color_id);
  const itemPrice = size ? parseFloat(size.price) : parseFloat(product.price);
  const totalItemPrice = itemPrice * item.quantity;

  const image =
    product.images.find(
      (img) => img.size_id === item.size_id || img.color_id === item.color_id,
    ) || product.images[0];

  return (
    <li className="flex items-center space-x-4 rounded-md bg-gray-50 p-4">
      <div className="h-16 w-16 flex-shrink-0 overflow-hidden rounded-md border border-gray-200">
        {image ? (
          <Image
            src={`https://storage.naayiq.com/resources/${image.url}`}
            alt={product.name}
            width={64}
            height={64}
            className="h-full w-full object-cover object-center"
          />
        ) : (
          <div className="flex h-full w-full items-center justify-center bg-gray-100 text-gray-400">
            No image
          </div>
        )}
      </div>
      <div className="flex-grow">
        <h4 className="font-medium">{product.name}</h4>
        <p className="text-sm text-gray-500">
          {size && `Size: ${size.name}`} {color && `Color: ${color.name}`}
        </p>
        <p className="text-sm">
          {item.quantity} x {formatPrice(itemPrice)} ={" "}
          {formatPrice(totalItemPrice)}
        </p>
      </div>
    </li>
  );
});

OrderDetails.displayName = "OrderDetails";
OrderItem.displayName = "OrderItem";

export default OrdersPage;