import { useCallback, useEffect, useRef, useState } from 'react';

import pb from '../lib/pocketbase';

/** Per-user record of opened listings, stored in PocketBase so visited listings can be drawn
 *  in a distinct colour on the map and persist across devices. One row per (user, url). */
const CLICKED_LISTINGS_COLLECTION = 'clicked_listings';

function currentUserId(): string | null {
  return pb.authStore.isValid && pb.authStore.record ? pb.authStore.record.id : null;
}

/** Tracks which listings the signed-in user has opened.
 *
 *  Reads the user from the shared PocketBase authStore so it stays self-contained — no need to
 *  thread a user id down through the map layer tree. Returns a Set for O(1) membership checks in
 *  the map's colour accessor; a fresh Set instance is produced on every change so it can double
 *  as a deck.gl `updateTrigger` (identity-compared).
 *
 *  Writes are optimistic: `markClicked` updates local state synchronously so the pin recolours
 *  the instant it is clicked, independent of the PocketBase round-trip or the next listings fetch.
 */
export function useClickedListings() {
  const [clickedUrls, setClickedUrls] = useState<Set<string>>(() => new Set());
  const [userId, setUserId] = useState<string | null>(currentUserId);

  const userIdRef = useRef(userId);
  userIdRef.current = userId;
  // Mirrors the latest committed set so markClicked can decide (synchronously) whether a url is
  // new — without depending on the async state updater having run yet.
  const clickedUrlsRef = useRef(clickedUrls);
  clickedUrlsRef.current = clickedUrls;

  // Follow login/logout via the shared authStore (also covers cross-tab auth changes).
  useEffect(() => {
    const unsubscribe = pb.authStore.onChange(() => setUserId(currentUserId()));
    return unsubscribe;
  }, []);

  // Load the user's previously opened listings whenever the signed-in user changes. Signed-out
  // users keep an in-memory set for the current session only (nothing to persist to).
  useEffect(() => {
    if (!userId) {
      setClickedUrls(new Set());
      return;
    }
    let cancelled = false;
    pb.collection(CLICKED_LISTINGS_COLLECTION)
      .getFullList({ filter: `user = "${userId}"`, fields: 'url' })
      .then((records) => {
        if (cancelled) return;
        const urls = records
          .map((record) => (record as { url?: unknown }).url)
          .filter((url): url is string => typeof url === 'string');
        setClickedUrls(new Set(urls));
      })
      .catch(() => {
        // Visited history is a convenience; a failed load just starts the session empty.
      });
    return () => {
      cancelled = true;
    };
  }, [userId]);

  const markClicked = useCallback((url: string | undefined | null) => {
    if (!url || clickedUrlsRef.current.has(url)) return; // unknown or already visited

    // Optimistic, synchronous local update → the map pin recolours on click.
    setClickedUrls((prev) => (prev.has(url) ? prev : new Set(prev).add(url)));

    const uid = userIdRef.current;
    if (!uid) return; // anonymous: recolour for the session, nothing to persist
    pb.collection(CLICKED_LISTINGS_COLLECTION)
      .create({ user: uid, url })
      .catch(() => {
        // Ignore duplicate-index conflicts and transient failures — local state already
        // reflects the click, and a missed write only means it won't persist to the next visit.
      });
  }, []);

  return { clickedUrls, markClicked };
}
