/* eslint-disable no-console */
import { useCallback, useEffect, useRef, useState } from "react";

// import { getRoomStatuses, getUserPlayerStatus } from "@/backend/player/status";
import { getRoomStatuses } from "@/backend/player/status";
import { useBackendUrl } from "@/hooks/auth/useBackendUrl";
import { useAuthStore } from "@/stores/auth";
import { usePlayerStore } from "@/stores/player/store";
import { useWatchPartyStore } from "@/stores/watchParty";

interface RoomUser {
  userId: string;
  isHost: boolean;
  lastUpdate: number;
  player: {
    isPlaying: boolean;
    isPaused: boolean;
    time: number;
    duration: number;
  };
  content: {
    title: string;
    type: string;
    tmdbId?: number;
    seasonId?: number;
    episodeId?: number;
    seasonNumber?: number;
    episodeNumber?: number;
  };
}

interface WatchPartySyncResult {
  // All users in the room
  roomUsers: RoomUser[];
  // The host user (if any)
  hostUser: RoomUser | null;
  // Whether our player is behind the host
  isBehindHost: boolean;
  // Whether our player is ahead of the host
  isAheadOfHost: boolean;
  // Seconds difference from host (positive means ahead, negative means behind)
  timeDifferenceFromHost: number;
  // Function to sync with host
  syncWithHost: () => void;
  // Whether we are currently syncing
  isSyncing: boolean;
  // Manually refresh room data
  refreshRoomData: () => Promise<void>;
  // Current user count in room
  userCount: number;
}

/**
 * Hook for syncing with other users in a watch party room
 */
export function useWatchPartySync(
  syncThresholdSeconds = 5,
): WatchPartySyncResult {
  const [roomUsers, setRoomUsers] = useState<RoomUser[]>([]);
  const [isSyncing, setIsSyncing] = useState(false);
  const [userCount, setUserCount] = useState(1);

  // Refs for tracking state
  const syncStateRef = useRef({
    lastUserCount: 1,
    previousHostPlaying: null as boolean | null,
    previousHostTime: null as number | null,
    lastSyncTime: 0,
    syncInProgress: false,
    checkedUrlParams: false,
    prevRoomUsers: [] as RoomUser[],
  });

  // Get our auth and backend info
  const account = useAuthStore((s) => s.account);
  const backendUrl = useBackendUrl();

  // Get player store functions
  const display = usePlayerStore((s) => s.display);
  const currentTime = usePlayerStore((s) => s.progress.time);
  const isPlaying = usePlayerStore((s) => s.mediaPlaying.isPlaying);
  // Get watch party state
  const { roomCode, isHost, enabled, enableAsGuest } = useWatchPartyStore();

  // Reset URL parameter checking when watch party is disabled
  useEffect(() => {
    if (!enabled) {
      syncStateRef.current.checkedUrlParams = false;
    }
  }, [enabled]);

  // Check URL parameters for watch party code
  useEffect(() => {
    if (syncStateRef.current.checkedUrlParams) return;

    try {
      const params = new URLSearchParams(window.location.search);
      const watchPartyCode = params.get("watchparty");

      if (watchPartyCode && !enabled && watchPartyCode.length > 0) {
        enableAsGuest(watchPartyCode);
      }

      syncStateRef.current.checkedUrlParams = true;
    } catch (error) {
      console.error("Failed to check URL parameters for watch party:", error);
    }
  }, [enabled, enableAsGuest]);

  // Find the host user in the room
  const hostUser = roomUsers.find((user) => user.isHost) || null;

  // Calculate predicted host time by accounting for elapsed time since update
  const getPredictedHostTime = useCallback(() => {
    if (!hostUser) return 0;

    const millisecondsSinceUpdate = Date.now() - hostUser.lastUpdate;
    const secondsSinceUpdate = millisecondsSinceUpdate / 1000;

    return hostUser.player.isPlaying && !hostUser.player.isPaused
      ? hostUser.player.time + secondsSinceUpdate
      : hostUser.player.time;
  }, [hostUser]);

  // Calculate time difference from host
  const timeDifferenceFromHost = hostUser
    ? currentTime - getPredictedHostTime()
    : 0;

  // Determine if we're ahead or behind the host
  const isBehindHost =
    hostUser && !isHost && timeDifferenceFromHost < -syncThresholdSeconds;
  const isAheadOfHost =
    hostUser && !isHost && timeDifferenceFromHost > syncThresholdSeconds;

  // Function to sync with host
  const syncWithHost = useCallback(() => {
    if (!hostUser || isHost || !display || syncStateRef.current.syncInProgress)
      return;

    syncStateRef.current.syncInProgress = true;
    setIsSyncing(true);

    const predictedHostTime = getPredictedHostTime();
    display.setTime(predictedHostTime);

    setTimeout(() => {
      if (hostUser.player.isPlaying && !hostUser.player.isPaused) {
        display.play();
      } else {
        display.pause();
      }

      setTimeout(() => {
        setIsSyncing(false);
        syncStateRef.current.syncInProgress = false;
      }, 500);

      syncStateRef.current.lastSyncTime = Date.now();
    }, 200);
  }, [hostUser, isHost, display, getPredictedHostTime]);

  // Combined effect for syncing time and play/pause state
  useEffect(() => {
    if (!hostUser || isHost || !display || syncStateRef.current.syncInProgress)
      return;

    const state = syncStateRef.current;
    const hostIsPlaying =
      hostUser.player.isPlaying && !hostUser.player.isPaused;
    const predictedHostTime = getPredictedHostTime();
    const difference = currentTime - predictedHostTime;

    // Handle time sync
    const activeThreshold = isPlaying ? 2 : 5;
    const needsTimeSync = Math.abs(difference) > activeThreshold;

    // Handle play state sync
    const needsPlayStateSync =
      state.previousHostPlaying !== null &&
      state.previousHostPlaying !== hostIsPlaying;

    // Handle time jumps
    const needsJumpSync =
      state.previousHostTime !== null &&
      Math.abs(hostUser.player.time - state.previousHostTime) > 5;

    // Sync if needed
    if ((needsTimeSync || needsPlayStateSync || needsJumpSync) && !isSyncing) {
      state.syncInProgress = true;
      setIsSyncing(true);

      // Sync time
      display.setTime(predictedHostTime);

      // Then sync play state after a short delay
      setTimeout(() => {
        if (hostIsPlaying) {
          display.play();
        } else {
          display.pause();
        }

        // Clear syncing flags
        setTimeout(() => {
          setIsSyncing(false);
          state.syncInProgress = false;
        }, 500);
      }, 200);
    }

    // Update state refs
    state.previousHostPlaying = hostIsPlaying;
    state.previousHostTime = hostUser.player.time;
  }, [
    hostUser,
    isHost,
    currentTime,
    display,
    isSyncing,
    getPredictedHostTime,
    isPlaying,
  ]);

  // Function to refresh room data
  const refreshRoomData = useCallback(async () => {
    if (!enabled || !roomCode || !backendUrl) return;

    try {
      const response = await getRoomStatuses(backendUrl, account, roomCode);
      const users: RoomUser[] = [];

      // Process each user's latest status
      Object.entries(response.users).forEach(
        ([userIdFromResponse, statuses]) => {
          if (statuses.length > 0) {
            // Get the latest status (sort by timestamp DESC)
            const latestStatus = [...statuses].sort(
              (a, b) => b.timestamp - a.timestamp,
            )[0];

            users.push({
              userId: userIdFromResponse,
              isHost: latestStatus.isHost,
              lastUpdate: latestStatus.timestamp,
              player: {
                isPlaying: latestStatus.player.isPlaying,
                isPaused: latestStatus.player.isPaused,
                time: latestStatus.player.time,
                duration: latestStatus.player.duration,
              },
              content: {
                title: latestStatus.content.title,
                type: latestStatus.content.type,
                tmdbId: latestStatus.content.tmdbId,
                seasonId: latestStatus.content.seasonId,
                episodeId: latestStatus.content.episodeId,
                seasonNumber: latestStatus.content.seasonNumber,
                episodeNumber: latestStatus.content.episodeNumber,
              },
            });
          }
        },
      );

      // Sort users with host first, then by lastUpdate
      users.sort((a, b) => {
        if (a.isHost && !b.isHost) return -1;
        if (!a.isHost && b.isHost) return 1;
        return b.lastUpdate - a.lastUpdate;
      });

      // Update user count if changed
      const newUserCount = users.length;
      if (newUserCount !== syncStateRef.current.lastUserCount) {
        setUserCount(newUserCount);
        syncStateRef.current.lastUserCount = newUserCount;
      }

      // Update room users
      syncStateRef.current.prevRoomUsers = users;
      setRoomUsers(users);
    } catch (error) {
      console.error("Failed to refresh room data:", error);
    }
  }, [backendUrl, account, roomCode, enabled]);

  // Periodically refresh room data
  useEffect(() => {
    // Store reference to current syncState for cleanup
    const syncState = syncStateRef.current;

    if (!enabled || !roomCode) {
      setRoomUsers([]);
      setUserCount(1);

      // Reset all state
      syncState.lastUserCount = 1;
      syncState.prevRoomUsers = [];
      syncState.previousHostPlaying = null;
      syncState.previousHostTime = null;
      return;
    }

    // Initial fetch
    refreshRoomData();

    // Set up interval - refresh every 1 second for faster updates
    const interval = setInterval(refreshRoomData, 1000);

    return () => {
      clearInterval(interval);
      setRoomUsers([]);
      setUserCount(1);

      // Use captured reference from outer scope
      syncState.previousHostPlaying = null;
      syncState.previousHostTime = null;
    };
  }, [enabled, roomCode, refreshRoomData]);

  return {
    roomUsers,
    hostUser,
    isBehindHost: !!isBehindHost,
    isAheadOfHost: !!isAheadOfHost,
    timeDifferenceFromHost,
    syncWithHost,
    isSyncing,
    refreshRoomData,
    userCount,
  };
}
