import { useCallback, useEffect, useState } from "react";
import { API_URL } from "../config/api.ts";
import type { Playlist, RawPlaylist } from "../model.ts";
import { deserializePlaylist, type PaginatedData } from "../model.ts";
import { useAuth } from "../hooks/useAuth.ts";
import { useWS } from "../hooks/useWS.ts";
import { useInfiniteScroll } from "../hooks/useInfiniteScroll.ts";
import { NewPlaylistForm } from "../components/NewPlaylistForm.tsx";
import { ConfirmModal } from "../components/ConfirmModal.tsx";
import { PlaylistCard } from "../components/PlaylistCard.tsx";
import { PageShell } from "../components/PageShell.tsx";

const PAGE_SIZE = 20;

type State =
  | { status: "loading" }
  | { status: "error"; error: string }
  | { status: "loaded"; playlists: Playlist[]; hasMore: boolean; page: number; loadingMore: boolean };

export function MyPlaylists() {
  const { user, authFetch, token } = useAuth();
  const { lastPlaylistEvent, deletedPlaylistIds } = useWS();
  const [state, setState] = useState<State>({ status: "loading" });
  const [confirmDeleteId, setConfirmDeleteId] = useState<string | null>(null);

  useEffect(() => {
    if (!user) return;
    fetch(`${API_URL}/api/users/${user.username}/playlists?page=1&limit=${PAGE_SIZE}`, {
      headers: { Authorization: `Bearer ${token}` },
    })
      .then((r) => r.json())
      .then((body) => {
        if (!body.success) throw new Error("Failed to load");
        const { items, hasMore } = body.data as PaginatedData<RawPlaylist>;
        setState({
          status: "loaded",
          playlists: items.map(deserializePlaylist),
          hasMore,
          page: 1,
          loadingMore: false,
        });
      })
      .catch((err) =>
        setState({
          status: "error",
          error: err instanceof Error ? err.message : "Failed to load playlists",
        })
      );
  }, [user?.username]);

  const loadMore = useCallback(() => {
    if (state.status !== "loaded" || !state.hasMore || state.loadingMore || !user) return;
    const nextPage = state.page + 1;
    setState((s) => s.status === "loaded" ? { ...s, loadingMore: true } : s);
    fetch(
      `${API_URL}/api/users/${user.username}/playlists?page=${nextPage}&limit=${PAGE_SIZE}`,
      { headers: { Authorization: `Bearer ${token}` } },
    )
      .then((r) => r.json())
      .then((body) => {
        const { items, hasMore } = body.data as PaginatedData<RawPlaylist>;
        setState((s) =>
          s.status === "loaded"
            ? {
              ...s,
              playlists: [...s.playlists, ...items.map(deserializePlaylist)],
              hasMore,
              page: nextPage,
              loadingMore: false,
            }
            : s
        );
      })
      .catch(() =>
        setState((s) => s.status === "loaded" ? { ...s, loadingMore: false } : s)
      );
  }, [state, user, token]);

  const sentinelRef = useInfiniteScroll(
    loadMore,
    state.status === "loaded" && state.hasMore && !state.loadingMore,
  );

  // Real-time WS updates
  useEffect(() => {
    if (!lastPlaylistEvent || !user) return;
    const ev = lastPlaylistEvent;

    if (ev.type === "created" && ev.playlist?.userId === user.id) {
      setState((s) => {
        if (s.status !== "loaded") return s;
        if (s.playlists.some((p) => p.id === ev.playlist!.id)) return s;
        return { ...s, playlists: [ev.playlist!, ...s.playlists] };
      });
    } else if (ev.type === "updated" && ev.playlist?.userId === user.id) {
      setState((s) =>
        s.status === "loaded"
          ? {
            ...s,
            playlists: s.playlists.map((p) =>
              p.id === ev.playlist!.id ? ev.playlist! : p
            ),
          }
          : s
      );
    } else if (ev.type === "deleted") {
      setState((s) =>
        s.status === "loaded"
          ? {
            ...s,
            playlists: s.playlists.filter((p) => p.id !== ev.playlistId),
          }
          : s
      );
    }
  }, [lastPlaylistEvent, user]);

  useEffect(() => {
    if (!deletedPlaylistIds.size) return;
    setState((s) =>
      s.status === "loaded"
        ? {
          ...s,
          playlists: s.playlists.filter((p) => !deletedPlaylistIds.has(p.id)),
        }
        : s
    );
  }, [deletedPlaylistIds]);

  const handleDelete = async (playlistId: string) => {
    await authFetch(`${API_URL}/api/playlists/${playlistId}`, {
      method: "DELETE",
    });
    setState((s) =>
      s.status === "loaded"
        ? { ...s, playlists: s.playlists.filter((p) => p.id !== playlistId) }
        : s
    );
  };

  return (
    <PageShell>
      <div className="my-playlists-header">
        <h1 className="my-playlists-title">My Playlists</h1>
        <NewPlaylistForm
          toggleClassName="btn-primary"
          onCreated={(p) =>
            setState((s) => {
              if (s.status !== "loaded") return s;
              if (s.playlists.some((pl) => pl.id === p.id)) return s;
              return { ...s, playlists: [p, ...s.playlists] };
            })}
        />
      </div>

      {state.status === "loading" && <p className="page-loading">Loading…</p>}
      {state.status === "error" && <p className="form-error">{state.error}</p>}
      {state.status === "loaded" && (
        state.playlists.length === 0
          ? <p className="empty-state">No playlists yet. Create one!</p>
          : (
            <ul className="dump-feed">
              {state.playlists.map((p) => (
                <PlaylistCard
                  key={p.id}
                  playlist={p}
                  onDelete={() => setConfirmDeleteId(p.id)}
                />
              ))}
            </ul>
          )
      )}

      <div ref={sentinelRef} />
      {state.status === "loaded" && state.loadingMore && (
        <p className="feed-loading-more">Loading more…</p>
      )}

      {confirmDeleteId && (
        <ConfirmModal
          message="Delete this playlist? This cannot be undone."
          confirmLabel="Delete playlist"
          onConfirm={() => {
            handleDelete(confirmDeleteId);
            setConfirmDeleteId(null);
          }}
          onCancel={() => setConfirmDeleteId(null)}
        />
      )}
    </PageShell>
  );
}
