/* ============================================================
   CADENCE — Client data store (React context)
   window.CadProvider / window.useCad — the single source of truth for
   app data (songs, portfolio, artist, market feed, user, genres) and the
   data actions (trade, watch, createOffering). The provider also mirrors
   window.CAD so any legacy reads stay consistent during/after updates.
   ============================================================ */
(function () {
  const { createContext, useContext, useState, useCallback, useMemo } = React;
  const CadContext = createContext(null);

  function watchlistSongs(ids) {
    const songs = window.CAD.songs || [];
    return ids.map((id) => songs.find((s) => s.id === id)).filter(Boolean);
  }

  function CadProvider({ children }) {
    const [songs, setSongs] = useState(() => window.CAD.songs || []);
    const [portfolio, setPortfolio] = useState(() => window.CAD.portfolio);
    const [artist, setArtist] = useState(() => window.CAD.artist);

    // Stable for the session (re-bootstrap via reload after auth changes).
    const marketFeed = window.CAD.marketFeed || [];
    const user = window.CAD.currentUser;
    const genres = window.CAD.GENRES || {};

    const trade = useCallback(async ({ songId, side, shares }) => {
      const data = await window.CadClient.trade({ songId, side, shares });
      setSongs((prev) => {
        const next = prev.map((s) => (s.id === data.song.id ? data.song : s));
        window.CAD.songs = next;
        return next;
      });
      setPortfolio(data.portfolio);
      window.CAD.portfolio = data.portfolio;
      return data;
    }, []);

    // Re-fetch the whole app payload (used after returning from Stripe Checkout,
    // once the webhook has recorded the purchase and flipped `owned`).
    const refresh = useCallback(async () => {
      const data = await window.CAD_API.loadCadence();
      setSongs(data.songs || []);
      setPortfolio(data.portfolio);
      setArtist(data.artist);
      return data;
    }, []);

    // Real purchases happen on Stripe's hosted Checkout — start a session and
    // send the buyer there. Fulfillment + audio unlock happen via the webhook.
    const buySong = useCallback(async (songId) => {
      const data = await window.CadClient.startSongCheckout(songId);
      if (data && data.url) {
        window.location.assign(data.url);
      }
      return data;
    }, []);

    const watch = useCallback(async (songId, add) => {
      const result = await window.CadClient.watch(songId, add);
      setPortfolio((prev) => {
        const next = { ...prev, watchlist: watchlistSongs(result.watchlist) };
        window.CAD.portfolio = next;
        return next;
      });
      return result.watchlist;
    }, []);

    const createOffering = useCallback(async (payload, file) => {
      const data = await window.CadClient.createOffering(payload, file);
      // New song exists server-side; reflect it locally too.
      setSongs((prev) => {
        const next = [...prev, data.song];
        window.CAD.songs = next;
        return next;
      });
      if (data.artist) {
        setArtist(data.artist);
        window.CAD.artist = data.artist;
      }
      return data;
    }, []);

    const addFunds = useCallback(async (amount) => {
      const data = await window.CadClient.addFunds(amount);
      setPortfolio(data.portfolio);
      window.CAD.portfolio = data.portfolio;
      return data;
    }, []);

    const deleteOffering = useCallback(async (songId) => {
      const data = await window.CadClient.deleteOffering(songId);
      setSongs((prev) => {
        const next = prev.filter((s) => s.id !== songId);
        window.CAD.songs = next;
        return next;
      });
      setArtist((prev) => {
        if (!prev) return prev;
        const next = {
          ...prev,
          songs: (prev.songs || []).filter((s) => s.id !== songId),
          singles: (prev.singles || []).filter((s) => s.id !== songId),
          releases: (prev.releases || []).map((r) => ({ ...r, songs: r.songs.filter((s) => s.id !== songId) })),
        };
        window.CAD.artist = next;
        return next;
      });
      return data;
    }, []);

    const applyArtist = (next) => { setArtist(next); window.CAD.artist = next; };

    const createRelease = useCallback(async (payload) => {
      const data = await window.CadClient.createRelease(payload);
      applyArtist(data.artist);
      return data;
    }, []);

    const updateRelease = useCallback(async (releaseId, patch) => {
      const data = await window.CadClient.updateRelease(releaseId, patch);
      applyArtist(data.artist);
      return data;
    }, []);

    const deleteRelease = useCallback(async (releaseId) => {
      const data = await window.CadClient.deleteRelease(releaseId);
      applyArtist(data.artist);
      return data;
    }, []);

    const assignSong = useCallback(async (songId, releaseId) => {
      const data = await window.CadClient.assignSong(songId, releaseId);
      applyArtist(data.artist);
      setSongs((prev) => {
        const next = prev.map((s) => (s.id === songId ? { ...s, releaseId: releaseId || null } : s));
        window.CAD.songs = next;
        return next;
      });
      return data;
    }, []);

    const buyRelease = useCallback(async (releaseId) => {
      const data = await window.CadClient.startReleaseCheckout(releaseId);
      if (data && data.url) {
        window.location.assign(data.url);
      }
      return data;
    }, []);

    const saveProfile = useCallback(async (payload) => {
      const data = await window.CadClient.updateProfile(payload);
      if (data.currentUser) {
        window.CAD.currentUser = data.currentUser;
      }
      return data;
    }, []);

    const value = useMemo(
      () => ({
        songs, portfolio, artist, marketFeed, user, genres,
        trade, buySong, addFunds, watch, createOffering, deleteOffering,
        createRelease, updateRelease, deleteRelease, assignSong, buyRelease, saveProfile, refresh,
      }),
      [songs, portfolio, artist, marketFeed, user, genres, trade, buySong, addFunds, watch, createOffering, deleteOffering, createRelease, updateRelease, deleteRelease, assignSong, buyRelease, saveProfile, refresh]
    );

    return <CadContext.Provider value={value}>{children}</CadContext.Provider>;
  }

  function useCad() {
    const ctx = useContext(CadContext);
    if (!ctx) {
      throw new Error('useCad must be used within a CadProvider');
    }
    return ctx;
  }

  window.CadProvider = CadProvider;
  window.useCad = useCad;
})();
