/* eslint-disable @typescript-eslint/no-explicit-any */
"use client";

import React, { createContext, useContext, useState, useEffect, useCallback, useMemo, useRef } from "react";
import { toast } from "sonner";
import { Product } from "@/types/product/product";
import { CartItem, CouponDetails, District, ChargeRule } from "@/types";
import { getClientProfileAction } from "@/actions/auth.actions";
import {
  syncCartAction,
  getCartAction,
  addToCartAction,
  updateCartItemQuantityAction,
  removeFromCartAction,
  clearCartAction,
} from "@/actions/cart/cart.action";

const BACKEND_URL = process.env.NEXT_PUBLIC_BACKEND_URL || "http://localhost:3001/api/v1";

// ─── Types ────────────────────────────────────────────────────────────────────



export const DISTRICTS: District[] = [
  { name: "Dhaka (Inside)", value: "dhaka", areaType: "inside_dhaka" },
  { name: "Gazipur", value: "gazipur", areaType: "sub_dhaka" },
  { name: "Narayanganj", value: "narayanganj", areaType: "sub_dhaka" },
  { name: "Savar", value: "savar", areaType: "sub_dhaka" },
  { name: "Bagerhat", value: "bagerhat", areaType: "outside_dhaka" },
  { name: "Bandarban", value: "bandarban", areaType: "outside_dhaka" },
  { name: "Barguna", value: "barguna", areaType: "outside_dhaka" },
  { name: "Barisal", value: "barisal", areaType: "outside_dhaka" },
  { name: "Bhola", value: "bhola", areaType: "outside_dhaka" },
  { name: "Bogra", value: "bogra", areaType: "outside_dhaka" },
  { name: "Brahmanbaria", value: "brahmanbaria", areaType: "outside_dhaka" },
  { name: "Chandpur", value: "chandpur", areaType: "outside_dhaka" },
  { name: "Chapainawabganj", value: "chapainawabganj", areaType: "outside_dhaka" },
  { name: "Chittagong", value: "chittagong", areaType: "outside_dhaka" },
  { name: "Chuadanga", value: "chuadanga", areaType: "outside_dhaka" },
  { name: "Comilla", value: "comilla", areaType: "outside_dhaka" },
  { name: "Cox's Bazar", value: "coxs_bazar", areaType: "outside_dhaka" },
  { name: "Dinajpur", value: "dinajpur", areaType: "outside_dhaka" },
  { name: "Faridpur", value: "faridpur", areaType: "outside_dhaka" },
  { name: "Feni", value: "feni", areaType: "outside_dhaka" },
  { name: "Gaibandha", value: "gaibandha", areaType: "outside_dhaka" },
  { name: "Gopalganj", value: "gopalganj", areaType: "outside_dhaka" },
  { name: "Habiganj", value: "habiganj", areaType: "outside_dhaka" },
  { name: "Jamalpur", value: "jamalpur", areaType: "outside_dhaka" },
  { name: "Jessore", value: "jessore", areaType: "outside_dhaka" },
  { name: "Jhalokati", value: "jhalokati", areaType: "outside_dhaka" },
  { name: "Jhenaidah", value: "jhenaidah", areaType: "outside_dhaka" },
  { name: "Joypurhat", value: "joypurhat", areaType: "outside_dhaka" },
  { name: "Khagrachhari", value: "khagrachhari", areaType: "outside_dhaka" },
  { name: "Khulna", value: "khulna", areaType: "outside_dhaka" },
  { name: "Kishoreganj", value: "kishoreganj", areaType: "outside_dhaka" },
  { name: "Kurigram", value: "kurigram", areaType: "outside_dhaka" },
  { name: "Kushtia", value: "kushtia", areaType: "outside_dhaka" },
  { name: "Lakshmipur", value: "lakshmipur", areaType: "outside_dhaka" },
  { name: "Lalmonirhat", value: "lalmonirhat", areaType: "outside_dhaka" },
  { name: "Madaripur", value: "madaripur", areaType: "outside_dhaka" },
  { name: "Magura", value: "magura", areaType: "outside_dhaka" },
  { name: "Manikganj", value: "manikganj", areaType: "outside_dhaka" },
  { name: "Meherpur", value: "meherpur", areaType: "outside_dhaka" },
  { name: "Moulvibazar", value: "moulvibazar", areaType: "outside_dhaka" },
  { name: "Munshiganj", value: "munshiganj", areaType: "outside_dhaka" },
  { name: "Mymensingh", value: "mymensingh", areaType: "outside_dhaka" },
  { name: "Naogaon", value: "naogaon", areaType: "outside_dhaka" },
  { name: "Narail", value: "narail", areaType: "outside_dhaka" },
  { name: "Narsingdi", value: "narsingdi", areaType: "outside_dhaka" },
  { name: "Natore", value: "natore", areaType: "outside_dhaka" },
  { name: "Netrokona", value: "netrokona", areaType: "outside_dhaka" },
  { name: "Nilphamari", value: "nilphamari", areaType: "outside_dhaka" },
  { name: "Noakhali", value: "noakhali", areaType: "outside_dhaka" },
  { name: "Pabna", value: "pabna", areaType: "outside_dhaka" },
  { name: "Panchagarh", value: "panchagarh", areaType: "outside_dhaka" },
  { name: "Patuakhali", value: "patuakhali", areaType: "outside_dhaka" },
  { name: "Pirojpur", value: "pirojpur", areaType: "outside_dhaka" },
  { name: "Rajbari", value: "rajbari", areaType: "outside_dhaka" },
  { name: "Rajshahi", value: "rajshahi", areaType: "outside_dhaka" },
  { name: "Rangamati", value: "rangamati", areaType: "outside_dhaka" },
  { name: "Rangpur", value: "rangpur", areaType: "outside_dhaka" },
  { name: "Satkhira", value: "satkhira", areaType: "outside_dhaka" },
  { name: "Shariatpur", value: "shariatpur", areaType: "outside_dhaka" },
  { name: "Sherpur", value: "sherpur", areaType: "outside_dhaka" },
  { name: "Sirajganj", value: "sirajganj", areaType: "outside_dhaka" },
  { name: "Sunamganj", value: "sunamganj", areaType: "outside_dhaka" },
  { name: "Sylhet", value: "sylhet", areaType: "outside_dhaka" },
  { name: "Tangail", value: "tangail", areaType: "outside_dhaka" },
  { name: "Thakurgaon", value: "thakurgaon", areaType: "outside_dhaka" }
];



interface CartContextType {
  cartItems: CartItem[];
  cartOpen: boolean;
  setCartOpen: (open: boolean) => void;
  addToCart: (product: Product, quantity?: number) => void;
  removeFromCart: (productId: number) => void;
  updateQuantity: (productId: number, quantity: number) => void;
  clearCart: () => void;
  coupon: CouponDetails | null;
  applyCoupon: (code: string) => Promise<boolean>;
  removeCoupon: () => void;
  city: string;
  setCity: (city: string) => void;
  paymentMethod: string;
  setPaymentMethod: (method: string) => void;
  isHydrated: boolean;
  isSyncing: boolean;
  calculations: {
    originalSubtotal: number;
    productDiscount: number;
    subtotal: number;
    couponDiscount: number;
    deliveryCharge: number;
    codCharge: number;
    total: number;
    matchedRuleId: number | null;
  };
}

// ─── Context ──────────────────────────────────────────────────────────────────

const CartContext = createContext<CartContextType | undefined>(undefined);

const parseNum = (v: unknown): number => {
  const n = parseFloat(String(v ?? ""));
  return isNaN(n) ? 0 : n;
};

// ─── Provider ─────────────────────────────────────────────────────────────────

export function CartProvider({ children }: { children: React.ReactNode }) {
  const [cartItems, setCartItems] = useState<CartItem[]>([]);
  const [cartOpen, setCartOpen] = useState(false);
  const [coupon, setCoupon] = useState<CouponDetails | null>(null);
  const [cityState, setCityState] = useState("");
  const [isLoggedIn, setIsLoggedIn] = useState(false);
  const [isSyncing, setIsSyncing] = useState(true);

  const setCity = useCallback((val: string) => {
    if (!val) {
      setCityState("");
      return;
    }
    const normalized = val.toLowerCase().replace(/[^a-z0-9]/g, "");
    const matched = DISTRICTS.find(d =>
      d.value.toLowerCase().replace(/[^a-z0-9]/g, "") === normalized ||
      d.name.toLowerCase().replace(/[^a-z0-9]/g, "") === normalized
    );
    setCityState(matched ? matched.value : val.toLowerCase().trim());
  }, []);

  const [paymentMethod, setPaymentMethod] = useState("cod");
  const [isHydrated, setIsHydrated] = useState(false);
  const [chargeRules, setChargeRules] = useState<ChargeRule[]>([]);

  useEffect(() => {
    const fetchChargeRules = async () => {
      try {
        const res = await fetch(`${BACKEND_URL}/charge/active`);
        if (res.ok) {
          const json = await res.json();
          const data = Array.isArray(json) ? json : json?.data || [];
          setChargeRules(data);
        }
      } catch (err: any) {
        console.warn("Failed to fetch charge rules (using local fallback calculations):", err.message || err);
      }
    };
    fetchChargeRules();
  }, []);

  // Function to check auth and handle cart syncing
  const syncDatabaseCart = useCallback(async (currentCart: CartItem[]) => {
    setIsSyncing(true);
    try {
      const profileRes = await getClientProfileAction();
      if (profileRes.success && profileRes.data) {
        setIsLoggedIn(true);

        if (currentCart.length > 0) {
          const syncRes = await syncCartAction(
            currentCart.map((item) => ({
              product_id: Number(item.product_id),
              quantity: item.quantity,
            }))
          );
          if (syncRes.success && Array.isArray(syncRes.data)) {
            const dbItems: CartItem[] = syncRes.data.map((dbItem: any) => {
              const product = dbItem.product || dbItem.variant?.product;
              const primaryImage =
                product?.images?.find((img: any) => img.is_primary)?.image_url ??
                product?.images?.[0]?.image_url ?? "";
              const price = Number(product?.final_price) || Number(product?.base_price) || Number(product?.price) || 0;
              const original_price = Number(product?.base_price) || price;

              return {
                product_id: String(product?.id || dbItem.product_id || ""),
                name: product?.name || "",
                product_name: product?.name || "",
                price,
                original_price,
                image: primaryImage,
                sku: product?.sku || "",
                quantity: dbItem.quantity,
                weight_grams: Number(product?.weight_grams) || 0,
                product_slug: product?.slug || "",
                emi_available: !!product?.emi_available,
              };
            });
            setCartItems(dbItems);
            localStorage.removeItem("cart");
          }
        } else {
          const getRes = await getCartAction();
          if (getRes.success && Array.isArray(getRes.data)) {
            const dbItems: CartItem[] = getRes.data.map((dbItem: any) => {
              const product = dbItem.product || dbItem.variant?.product;
              const primaryImage =
                product?.images?.find((img: any) => img.is_primary)?.image_url ??
                product?.images?.[0]?.image_url ?? "";
              const price = Number(product?.final_price) || Number(product?.base_price) || Number(product?.price) || 0;
              const original_price = Number(product?.base_price) || price;

              return {
                product_id: String(product?.id || dbItem.product_id || ""),
                name: product?.name || "",
                product_name: product?.name || "",
                price,
                original_price,
                image: primaryImage,
                sku: product?.sku || "",
                quantity: dbItem.quantity,
                weight_grams: Number(product?.weight_grams) || 0,
                product_slug: product?.slug || "",
                emi_available: !!product?.emi_available,
              };
            });
            setCartItems(dbItems);
          }
        }
      } else {
        setIsLoggedIn(false);
      }
    } catch (err) {
      console.error("Error syncing database cart", err);
    } finally {
      setIsSyncing(false);
    }
  }, []);

  // ── Hydrate from localStorage ──────────────────────────────────────────────
  useEffect(() => {
    const savedCart = localStorage.getItem("cart");
    const savedCity = localStorage.getItem("cart_city");
    const savedCoupon = localStorage.getItem("cart_coupon");

    setTimeout(() => {
      let parsedCart: CartItem[] = [];
      if (savedCart) {
        try {
          parsedCart = JSON.parse(savedCart);
          setCartItems(parsedCart);
        }
        catch (e) { console.error("Failed to parse cart", e); }
      }
      if (savedCity) setCity(savedCity);
      if (savedCoupon && parsedCart.length > 0) {
        try { setCoupon(JSON.parse(savedCoupon)); } catch { }
      } else {
        localStorage.removeItem("cart_coupon");
      }
      setIsHydrated(true);
    }, 0);
  }, [setCity]);

  // Ref to hold latest cartItems without causing effect re-runs
  const cartItemsRef = useRef(cartItems);
  useEffect(() => { cartItemsRef.current = cartItems; }, [cartItems]);

  // Sync DB cart ONCE when hydration finishes
  const hasSyncedRef = useRef(false);
  useEffect(() => {
    if (isHydrated && !hasSyncedRef.current) {
      hasSyncedRef.current = true;
      syncDatabaseCart(cartItemsRef.current);
    }
  }, [isHydrated, syncDatabaseCart]);

  // Listen to profile updates (e.g. login/logout) — re-sync on login/logout events
  useEffect(() => {
    const handleProfileUpdate = () => {
      syncDatabaseCart(cartItemsRef.current);
    };
    window.addEventListener("profileUpdated", handleProfileUpdate);
    return () => window.removeEventListener("profileUpdated", handleProfileUpdate);
  }, [syncDatabaseCart]);

  // ── Persist to localStorage (only if not logged in, otherwise use DB) ───────
  useEffect(() => {
    if (isHydrated && !isLoggedIn) {
      localStorage.setItem("cart", JSON.stringify(cartItems));
    }
  }, [cartItems, isHydrated, isLoggedIn]);

  useEffect(() => {
    if (isHydrated) localStorage.setItem("cart_city", cityState);
  }, [cityState, isHydrated]);

  useEffect(() => {
    if (!isHydrated) return;
    if (coupon) localStorage.setItem("cart_coupon", JSON.stringify(coupon));
    else localStorage.removeItem("cart_coupon");
  }, [coupon, isHydrated]);

  // ── Cart methods ───────────────────────────────────────────────────────────

  const addToCart = async (product: Product, quantity = 1) => {
    const productId = Number(product.id);
    const primaryImage =
      product.images?.find((img) => img.is_primary)?.image_url ??
      product.images?.[0]?.image_url ?? "/placeholder-product.svg";

    const price = Number(product.final_price) || Number(product.base_price) || 0;
    const original_price = Number(product.base_price) || price;

    const newItem: CartItem = {
      product_id: String(product.id),
      name: product.name,
      product_name: product.name,
      price,
      original_price,
      image: primaryImage,
      sku: "",
      quantity,
      weight_grams: 0,
      product_slug: product.slug,
      emi_available: !!product.emi_available,
    };

    setCartItems((prev) => {
      const existing = prev.find((item) => Number(item.product_id) === productId);
      if (existing) {
        return prev.map((item) =>
          Number(item.product_id) === productId
            ? { ...item, quantity: item.quantity + quantity }
            : item
        );
      }
      return [...prev, newItem];
    });
    setCartOpen(true);

    if (isLoggedIn) {
      await addToCartAction(productId, quantity);
    }
  };

  const removeFromCart = async (productId: number) => {
    const itemExists = cartItems.some((item) => Number(item.product_id) === productId);
    if (itemExists && cartItems.length === 1) {
      setCoupon(null);
    }
    setCartItems((prev) => prev.filter((item) => Number(item.product_id) !== productId));

    if (isLoggedIn) {
      await removeFromCartAction(productId);
    }
  };

  const updateQuantity = async (productId: number, quantity: number) => {
    if (quantity < 1) return;
    setCartItems((prev) =>
      prev.map((item) =>
        Number(item.product_id) === productId ? { ...item, quantity } : item
      )
    );

    if (isLoggedIn) {
      await updateCartItemQuantityAction(productId, quantity);
    }
  };

  const clearCart = async () => {
    setCartItems([]);
    setCoupon(null);
    if (typeof window !== "undefined") {
      localStorage.removeItem("cart");
      localStorage.removeItem("cart_coupon");
    }

    if (isLoggedIn) {
      await clearCartAction();
    }
  };

  // ── Calculations ───────────────────────────────────────────────────────────

  const subtotal = useMemo(() => {
    return cartItems.reduce((sum, item) => sum + item.price * item.quantity, 0);
  }, [cartItems]);

  const calculations = useMemo(() => {
    const originalSubtotal = cartItems.reduce(
      (sum, item) => sum + item.original_price * item.quantity,
      0
    );
    const productDiscount = originalSubtotal - subtotal;



    const selectedDistrict = DISTRICTS.find(
      (d) => d.value === (cityState || "dhaka").toLowerCase().trim()
    ) || DISTRICTS[0];
    const areaType = selectedDistrict.areaType;

    const totalWeightGrams = cartItems.reduce(
      (sum, item) => sum + (item.weight_grams || 0) * item.quantity,
      0
    );

    // First, try to match specific city/district value (e.g. 'sylhet', 'chittagong')
    let areaRules = chargeRules.filter((rule) => {
      const ruleArea = (rule.area_type || "").toLowerCase().trim().replace(/[^a-z0-9]/g, "");
      const targetCity = (cityState || "dhaka").toLowerCase().trim().replace(/[^a-z0-9]/g, "");
      return ruleArea === targetCity;
    });

    // If no specific city rules, fallback to general areaType ('inside_dhaka', 'sub_dhaka', 'outside_dhaka')
    if (areaRules.length === 0) {
      areaRules = chargeRules.filter((rule) => {
        const ruleArea = (rule.area_type || "").toLowerCase().trim().replace(/[^a-z0-9]/g, "");
        const targetArea = areaType.toLowerCase().trim().replace(/[^a-z0-9]/g, "");
        return ruleArea === targetArea;
      });
    }

    let matchedRule = areaRules.find((rule) => {
      const minW = parseNum(rule.min_weight_grams);
      const maxW = rule.max_weight_grams !== null && rule.max_weight_grams !== undefined ? parseNum(rule.max_weight_grams) : null;
      if (totalWeightGrams < minW) return false;
      if (maxW !== null && totalWeightGrams > maxW) return false;
      return true;
    });

    // If weight exceeds all rule ranges, select the rule with the highest weight range limit
    if (!matchedRule && areaRules.length > 0) {
      matchedRule = [...areaRules].sort((a, b) => {
        const maxA =
          a.max_weight_grams !== null && a.max_weight_grams !== undefined
            ? parseNum(a.max_weight_grams)
            : Infinity;
        const maxB =
          b.max_weight_grams !== null && b.max_weight_grams !== undefined
            ? parseNum(b.max_weight_grams)
            : Infinity;
        if (maxA === maxB) return 0;
        return maxA > maxB ? -1 : 1;
      })[0];
    }

    let deliveryCharge = 0;
    if (cartItems.length > 0) {
      if (matchedRule) {
        const baseCharge = parseNum(matchedRule.base_charge);
        const additionalPerKg = parseNum(matchedRule.additional_per_kg);
        const maxW = matchedRule.max_weight_grams !== null && matchedRule.max_weight_grams !== undefined ? parseNum(matchedRule.max_weight_grams) : null;

        let additionalCharge = 0;
        if (maxW !== null && totalWeightGrams > maxW && additionalPerKg > 0) {
          const extraWeightGrams = totalWeightGrams - maxW;
          const extraKg = Math.ceil(extraWeightGrams / 1000);
          additionalCharge = extraKg * additionalPerKg;
        }
        deliveryCharge = baseCharge + additionalCharge;
      } else {
        deliveryCharge = areaType === "inside_dhaka" ? 60 : areaType === "sub_dhaka" ? 80 : 120;
      }
    }

    const isFreeDeliveryCoupon = coupon && coupon.discount_type === "free_delivery";

    if (isFreeDeliveryCoupon) {
      deliveryCharge = 0;
    }

    let codCharge = 0;
    if (cartItems.length > 0 && !isFreeDeliveryCoupon) {
      if (matchedRule) {
        const codType = matchedRule.cod_charge_type?.toLowerCase().trim();
        const codVal = parseNum(matchedRule.cod_charge_value);
        if (codType === "fixed") {
          codCharge = codVal;
        } else if (codType === "percent" || codType === "percentage") {
          codCharge = (subtotal * codVal) / 100;
        }
      } else {
        // Fallback COD charge when rules fetch fails
        codCharge = areaType === "inside_dhaka" ? 30 : areaType === "sub_dhaka" ? 40 : 50;
      }
    }

    let couponDiscount = 0;
    if (coupon && cartItems.length > 0) {
      if (typeof coupon.discount_amount === "number") {
        couponDiscount = coupon.discount_amount;
      } else if (coupon.discount_type === "percentage" && coupon.discount_value) {
        couponDiscount = (subtotal * parseNum(coupon.discount_value)) / 100;
        if (coupon.max_discount_amount)
          couponDiscount = Math.min(couponDiscount, parseNum(coupon.max_discount_amount));
      } else if (coupon.discount_type === "fixed" && coupon.discount_value) {
        couponDiscount = parseNum(coupon.discount_value);
      }
    }
    couponDiscount = Math.min(couponDiscount, subtotal);
    const total = Math.max(0, subtotal - couponDiscount + deliveryCharge + (paymentMethod === "cod" ? codCharge : 0));

    return {
      originalSubtotal,
      productDiscount,
      subtotal,
      couponDiscount,
      deliveryCharge,
      codCharge,
      total,
      matchedRuleId: matchedRule ? Number(matchedRule.id) : null,
    };
  }, [cartItems, subtotal, chargeRules, cityState, coupon, paymentMethod]);

  // ── Coupon actions ─────────────────────────────────────────────────────────

  const applyCoupon = async (code: string): Promise<boolean> => {
    if (cartItems.length === 0) {
      toast.error("Your cart is empty");
      return false;
    }

    try {
      // Direct browser → NestJS fetch — visible in DevTools Network tab
      const res = await fetch(`${BACKEND_URL}/storefront/coupons/validate`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ code, order_amount: subtotal }),
      });

      if (!res.ok) {
        const errJson = await res.json().catch(() => null);
        const msg =
          (typeof errJson?.message === "string" ? errJson.message : null) ||
          (Array.isArray(errJson?.message) ? errJson.message.join(", ") : null) ||
          "Coupon is not valid";
        toast.error(msg);
        return false;
      }

      const json = await res.json();
      // Backend wraps: { success: true, data: { valid: bool, ... }, timestamp }
      const payload = json?.data ?? json;

      if (!payload.valid) {
        toast.error(String(payload.reason || payload.message || "Coupon is not valid"));
        return false;
      }

      // Coupon object is nested under payload.coupon
      const couponData = payload.coupon ?? payload;

      // discount_amount may be a string ("100.00") from TypeORM — parse both
      let discountAmount = parseNum(payload.discount_amount);

      // Fallback: compute client-side if backend returned 0 or omitted it
      if (discountAmount === 0) {
        const dv = parseNum(couponData.discount_value);
        if (couponData.discount_type === "percentage" && dv > 0) {
          discountAmount = (subtotal * dv) / 100;
          const max = parseNum(couponData.max_discount_amount);
          if (max > 0) discountAmount = Math.min(discountAmount, max);
        } else if (couponData.discount_type === "fixed" && dv > 0) {
          discountAmount = dv;
        }
      }

      setCoupon({
        id: couponData.id,
        code: couponData.code || code,
        discount_type: couponData.discount_type,
        discount_value: couponData.discount_value,
        discount_amount: discountAmount > 0 ? discountAmount : undefined,
        max_discount_amount: couponData.max_discount_amount,
      });

      toast.success(
        discountAmount > 0
          ? `Coupon "${couponData.code || code}" applied — you saved ৳${discountAmount.toFixed(0)}!`
          : `Coupon "${couponData.code || code}" applied!`
      );
      return true;

    } catch {
      toast.error("Coupon is not valid");
      return false;
    }
  };

  const removeCoupon = () => setCoupon(null);

  // ── Provider value ─────────────────────────────────────────────────────────

  return (
    <CartContext.Provider
      value={{
        cartItems,
        cartOpen,
        setCartOpen,
        addToCart,
        removeFromCart,
        updateQuantity,
        clearCart,
        coupon,
        applyCoupon,
        removeCoupon,
        city: cityState,
        setCity,
        paymentMethod,
        setPaymentMethod,
        calculations,
        isHydrated,
        isSyncing,
      }}
    >
      {children}
    </CartContext.Provider>
  );
}

// ─── Hook ─────────────────────────────────────────────────────────────────────

export function useCart() {
  const context = useContext(CartContext);
  if (context === undefined)
    throw new Error("useCart must be used within a CartProvider");
  return context;
}
