"use client";

import { useCallback, useMemo, useState } from "react";
import { SearchBar } from "./search-bar";
import { SearchResults } from "./search-results";
import { PosterGrid } from "./poster-grid";
import { useGroupMovies } from "@/hooks/use-group-movies";
import { useMovieSearch } from "@/hooks/use-movie-search";
import { useAddMovie } from "@/hooks/use-add-movie";
import { useRealtimeMovies } from "@/hooks/use-realtime-movies";
import { useRoll } from "@/hooks/use-roll";
import { useToggleWatched } from "@/hooks/use-toggle-watched";
import { RollBar } from "@/components/dice/roll-bar";
import { RollAnnouncer } from "@/components/dice/roll-announcer";
import { ListRollCarousel } from "@/components/dice/list-roll-carousel";
import { GenreRollModal } from "@/components/dice/genre-roll-modal";
import { filterByGenresAndEmotionsStructured } from "@/lib/dice/genre-filter";
import type { Database } from "@/types/database";

type MovieRow = Database["public"]["Tables"]["movies"]["Row"];

interface MemberInfo {
  id: string;
  displayName: string;
  avatarColor: string | null;
}

interface MovieListClientProps {
  groupId: string;
  groupName: string;
  members: MemberInfo[];
}

export function MovieListClient({ groupId, members }: MovieListClientProps) {
  const [searchQuery, setSearchQuery] = useState("");
  const [resultsHidden, setResultsHidden] = useState(false);
  const [addingTmdbId, setAddingTmdbId] = useState<number | null>(null);
  const [genreModalOpen, setGenreModalOpen] = useState(false);
  const [noMatchesBanner, setNoMatchesBanner] = useState(false);

  const { data, isLoading } = useGroupMovies(groupId);
  const allMovies = useMemo(() => data?.pages.flat() ?? [], [data]);

  const unwatchedPool = useMemo(() => allMovies.filter((m) => !m.watched), [allMovies]);
  const poolEmpty = unwatchedPool.length === 0;

  const { data: searchData, isLoading: isSearchLoading } = useMovieSearch(searchQuery);
  const tmdbResults = searchData?.results ?? [];

  const addMovie = useAddMovie();
  const toggleWatched = useToggleWatched(groupId);
  const { result: winner, rollState, roll } = useRoll();

  useRealtimeMovies(groupId);

  const users = useMemo(
    () => members.map((m) => ({ id: m.id, avatar_color: m.avatarColor })),
    [members],
  );

  const userNames = useMemo(() => {
    const m = new Map<string, string>();
    for (const member of members) m.set(member.id, member.displayName);
    return m;
  }, [members]);

  const handleSearch = useCallback((query: string) => {
    setSearchQuery(query);
    setResultsHidden(false);
  }, []);

  const handleAdd = useCallback(
    (tmdbId: number) => {
      setAddingTmdbId(tmdbId);
      setResultsHidden(true);
      addMovie.mutate(
        { tmdb_id: tmdbId, group_id: groupId },
        {
          onSettled: () => setAddingTmdbId(null),
        },
      );
    },
    [addMovie, groupId],
  );

  const handleRandomRoll = useCallback(() => {
    if (isLoading || poolEmpty) return;
    setNoMatchesBanner(false);
    roll(unwatchedPool);
  }, [isLoading, poolEmpty, unwatchedPool, roll]);

  const handleGenreRollOpen = useCallback(() => {
    if (isLoading || poolEmpty) return;
    setGenreModalOpen(true);
  }, [isLoading, poolEmpty]);

  const handleGenreRollSubmit = useCallback(
    (payload: { genreIds: number[]; moodKeys: string[] }) => {
      setGenreModalOpen(false);
      const { movies: filtered, noMatches } = filterByGenresAndEmotionsStructured(
        payload,
        unwatchedPool,
      );
      if (noMatches) {
        setNoMatchesBanner(true);
        roll(unwatchedPool);
        return;
      }
      setNoMatchesBanner(false);
      roll(filtered);
    },
    [unwatchedPool, roll],
  );

  const handleWatchedToggle = useCallback(
    (movie: MovieRow) => {
      toggleWatched.mutate({ movieId: movie.id, watched: !movie.watched });
    },
    [toggleWatched],
  );

  return (
    <div className="space-y-6">
      <RollBar
        isLoading={isLoading}
        poolEmpty={poolEmpty}
        onRoll={handleRandomRoll}
        onGenreRoll={handleGenreRollOpen}
      />

      <div>
        <SearchBar onSearch={handleSearch} isLoading={isSearchLoading} />
        {addMovie.isError && (
          <p role="alert" aria-live="polite" className="mt-2 text-sm text-red-500">
            Couldn&apos;t add movie. Please try again.
            {addMovie.error?.message ? ` (${addMovie.error.message})` : ""}
          </p>
        )}
        {searchQuery.length >= 2 && !resultsHidden && (
          <div className="mt-2 rounded-lg border border-white/10 bg-white/5 p-3">
            <SearchResults
              tmdbResults={tmdbResults}
              groupMovies={allMovies}
              query={searchQuery}
              isAdding={addMovie.isPending}
              addingTmdbId={addingTmdbId}
              onAdd={handleAdd}
            />
          </div>
        )}
      </div>

      <RollAnnouncer state={rollState} winner={winner} />

      {noMatchesBanner && (
        <div
          role="status"
          aria-live="polite"
          className="rounded-lg border border-yellow-500/40 bg-yellow-500/10 px-3 py-2 text-sm text-yellow-200"
          data-testid="roll-no-matches-banner"
        >
          No matches — showing full list
        </div>
      )}

      {rollState !== "idle" && (
        <ListRollCarousel
          pool={allMovies}
          winner={winner}
          state={rollState}
          onWatchedToggle={handleWatchedToggle}
        />
      )}

      <section aria-label="Movie list" aria-live="polite">
        <PosterGrid groupId={groupId} users={users} userNames={userNames} />
      </section>

      {genreModalOpen && (
        <GenreRollModal
          onClose={() => setGenreModalOpen(false)}
          onRoll={handleGenreRollSubmit}
          loading={false}
        />
      )}
    </div>
  );
}
