"use client";

import { useEffect, useState } from "react";
import { useRouter } from "next/navigation";
import { Bookmark, Loader2 } from "lucide-react";
import { toast } from "sonner";

import { customerApi } from "@/lib/api/customer";
import { useToken } from "@/lib/auth/useToken";

type Props = {
  commodityProductId: number;
  sellerProductId: number;
  /** Where to return after signing in. */
  returnTo: string;
};

/**
 * Watchlist toggle for the product detail page, ported from the legacy
 * `bookmark()` handler in `biznie-app/src/View/Products/ProductDetail.js`.
 *
 * A bookmark is keyed on (buyer, commodity product, seller listing), so the
 * button belongs to the offer being shown rather than the product — opening a
 * different seller's offer is a different row.
 *
 * The saved state is fetched here rather than read off the product payload:
 * that request is cached and shared between users, so it cannot carry anything
 * per-buyer. Until the fetch lands the button renders unsaved, which is also
 * what a signed-out visitor sees.
 */
export default function WatchlistButton({
  commodityProductId,
  sellerProductId,
  returnTo,
}: Props) {
  const router = useRouter();
  const { token, ready } = useToken();

  const [saved, setSaved] = useState(false);
  const [pending, setPending] = useState(false);

  useEffect(() => {
    if (!ready || !token) return;

    // Aborted on unmount so a slow status read cannot resolve onto a button
    // the buyer has already navigated away from.
    const controller = new AbortController();
    customerApi
      .watchlistStatus(
        token,
        { commodity_product_id: commodityProductId, seller_commodity_product_id: sellerProductId },
        controller.signal
      )
      .then((r) => setSaved(r.bookmarked))
      .catch(() => {
        // A failed lookup is not worth surfacing — the toggle still works and
        // the response tells us the true state.
      });

    return () => controller.abort();
  }, [ready, token, commodityProductId, sellerProductId]);

  async function toggle() {
    if (!token) {
      router.push(`/login?redirect=${encodeURIComponent(returnTo)}`);
      return;
    }

    setPending(true);
    // Optimistic: the toggle is a single row write, and the response below
    // corrects the flag if the server disagrees.
    const next = !saved;
    setSaved(next);

    try {
      const r = await customerApi.toggleWatchlist(token, {
        commodity_product_id: commodityProductId,
        seller_commodity_product_id: sellerProductId,
      });
      setSaved(r.bookmarked);
      toast.success(r.message);
    } catch (e) {
      setSaved(!next);
      toast.error(e instanceof Error ? e.message : "Could not update watchlist");
    } finally {
      setPending(false);
    }
  }

  const label = saved ? "Remove from watchlist" : "Add to watchlist";

  /*
    Icon only, and a fixed square: it sits beside the product title, where a
    label that changed width between "Watchlist" and "Saved" would shove the
    heading around on every toggle. The outcome goes to a toast, which lives
    outside the layout entirely — the earlier inline message was what made the
    button jump between rows.

    `title` + `aria-label` carry the meaning the removed text used to.
  */
  return (
    <button
      type="button"
      onClick={toggle}
      disabled={pending}
      aria-pressed={saved}
      aria-label={label}
      title={label}
      className={`inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-lg border transition disabled:opacity-60 ${
        saved
          ? "border-[#CC1414] bg-red-50 text-[#CC1414]"
          : "border-gray-200 bg-white text-gray-700 hover:bg-gray-50"
      }`}
    >
      {pending ? (
        <Loader2 size={17} className="animate-spin" />
      ) : (
        <Bookmark size={17} className={saved ? "fill-[#CC1414]" : ""} />
      )}
    </button>
  );
}
