import { useState, useCallback, useRef, useEffect } from "react";
import {
  Room,
  RoomEvent,
  VideoPresets,
  DataPacket_Kind,
  createLocalVideoTrack,
  createLocalAudioTrack,
} from "livekit-client";

const LIVEKIT_URL = "wss://stream.sociowire.com";
const LIVEKIT_API = "https://stream.sociowire.com";

/**
 * Hook for LiveKit-based live streaming
 * Much more reliable than raw WebRTC - handles TURN/ICE automatically
 */
export function useLiveKit({ roomName, userID, username, token: authToken, onDataReceived }) {
  const [isLive, setIsLive] = useState(false);
  const [isConnecting, setIsConnecting] = useState(false);
  const [error, setError] = useState(null);
  const [viewerCount, setViewerCount] = useState(0);
  const [participants, setParticipants] = useState([]);

  const roomRef = useRef(null);
  const localVideoTrackRef = useRef(null);
  const localAudioTrackRef = useRef(null);
  const onDataReceivedRef = useRef(onDataReceived);

  // Keep callback ref up to date
  useEffect(() => {
    onDataReceivedRef.current = onDataReceived;
  }, [onDataReceived]);

  // Get LiveKit token from backend
  const getToken = useCallback(async (isHost = true) => {
    try {
      const res = await fetch(`${LIVEKIT_API}/api/livekit/token`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),
        },
        body: JSON.stringify({
          room: roomName,
          identity: userID,
          name: username,
          canPublish: isHost,
          canSubscribe: true,
        }),
      });

      if (!res.ok) {
        throw new Error("Failed to get LiveKit token");
      }

      const data = await res.json();
      return data.token;
    } catch (err) {
      console.error("[LiveKit] Token error:", err);
      throw err;
    }
  }, [roomName, userID, username, authToken]);

  // Start broadcasting
  const startBroadcast = useCallback(async (videoTrack, audioEnabled = true) => {
    if (isConnecting || isLive) return;

    setIsConnecting(true);
    setError(null);

    try {
      // Get token
      const token = await getToken(true);

      // Create room
      const room = new Room({
        adaptiveStream: true,
        dynacast: true,
        videoCaptureDefaults: {
          resolution: VideoPresets.h720.resolution,
        },
      });

      // Set up event handlers
      room.on(RoomEvent.ParticipantConnected, (participant) => {
        console.log("[LiveKit] Participant joined:", participant.identity);
        setParticipants((prev) => [
          ...prev.filter((p) => p.identity !== participant.identity),
          {
            identity: participant.identity,
            name: participant.name || participant.identity,
          },
        ]);
        setViewerCount((prev) => prev + 1);
      });

      room.on(RoomEvent.ParticipantDisconnected, (participant) => {
        console.log("[LiveKit] Participant left:", participant.identity);
        setParticipants((prev) =>
          prev.filter((p) => p.identity !== participant.identity)
        );
        setViewerCount((prev) => Math.max(0, prev - 1));
      });

      room.on(RoomEvent.Disconnected, () => {
        console.log("[LiveKit] Disconnected from room");
        setIsLive(false);
      });

      room.on(RoomEvent.Reconnecting, () => {
        console.log("[LiveKit] Reconnecting...");
      });

      room.on(RoomEvent.Reconnected, () => {
        console.log("[LiveKit] Reconnected");
      });

      // Handle data channel messages (for live comments)
      room.on(RoomEvent.DataReceived, (payload, participant) => {
        try {
          const decoder = new TextDecoder();
          const jsonStr = decoder.decode(payload);
          const data = JSON.parse(jsonStr);
          console.log("[LiveKit] Data received:", data, "from:", participant?.identity);
          if (onDataReceivedRef.current) {
            onDataReceivedRef.current(data, participant);
          }
        } catch (err) {
          console.warn("[LiveKit] Failed to parse data:", err);
        }
      });

      // Connect to room
      await room.connect(LIVEKIT_URL, token);
      console.log("[LiveKit] Connected to room:", room.name);

      // Create and publish video track from provided stream
      if (videoTrack) {
        // If a MediaStreamTrack is provided, use it
        const track = videoTrack instanceof MediaStreamTrack
          ? videoTrack
          : videoTrack.getVideoTracks()[0];

        if (track) {
          await room.localParticipant.publishTrack(track, {
            name: "camera",
            simulcast: true,
            videoCodec: "vp8",
          });
          localVideoTrackRef.current = track;
        }
      } else {
        // Create default camera track
        const videoTrack = await createLocalVideoTrack({
          facingMode: "user",
          resolution: VideoPresets.h720.resolution,
        });
        await room.localParticipant.publishTrack(videoTrack);
        localVideoTrackRef.current = videoTrack;
      }

      // Create and publish audio track
      if (audioEnabled) {
        const audioTrack = await createLocalAudioTrack();
        await room.localParticipant.publishTrack(audioTrack);
        localAudioTrackRef.current = audioTrack;
      }

      roomRef.current = room;
      setIsLive(true);
      console.log("[LiveKit] Broadcasting started");

      // Start egress recording
      try {
        await fetch(`${LIVEKIT_API}/api/livekit/egress/start`, {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ room: roomName }),
        });
        console.log("[LiveKit] Egress recording started");
      } catch (err) {
        console.warn("[LiveKit] Failed to start egress:", err);
      }
    } catch (err) {
      console.error("[LiveKit] Failed to start broadcast:", err);
      setError(err.message || "Failed to start broadcast");
    } finally {
      setIsConnecting(false);
    }
  }, [isConnecting, isLive, getToken]);

  // Stop broadcasting
  const stopBroadcast = useCallback(async () => {
    if (roomRef.current) {
      // Unpublish tracks
      if (localVideoTrackRef.current) {
        roomRef.current.localParticipant.unpublishTrack(localVideoTrackRef.current);
        localVideoTrackRef.current.stop();
        localVideoTrackRef.current = null;
      }
      if (localAudioTrackRef.current) {
        roomRef.current.localParticipant.unpublishTrack(localAudioTrackRef.current);
        localAudioTrackRef.current.stop();
        localAudioTrackRef.current = null;
      }

      // Disconnect from room
      await roomRef.current.disconnect();
      roomRef.current = null;
    }

    setIsLive(false);
    setViewerCount(0);
    setParticipants([]);
    console.log("[LiveKit] Broadcast stopped");
  }, []);

  // Toggle audio
  const toggleAudio = useCallback((muted) => {
    if (localAudioTrackRef.current) {
      localAudioTrackRef.current.enabled = !muted;
    }
  }, []);

  // Toggle video
  const toggleVideo = useCallback((disabled) => {
    if (localVideoTrackRef.current) {
      localVideoTrackRef.current.enabled = !disabled;
    }
  }, []);

  // Send data to all participants (for live comments)
  const sendData = useCallback(async (data) => {
    if (!roomRef.current) return false;
    try {
      const encoder = new TextEncoder();
      const payload = encoder.encode(JSON.stringify(data));
      await roomRef.current.localParticipant.publishData(payload, DataPacket_Kind.RELIABLE);
      console.log("[LiveKit] Data sent:", data);
      return true;
    } catch (err) {
      console.error("[LiveKit] Failed to send data:", err);
      return false;
    }
  }, []);

  // Replace video track (for camera switch)
  const replaceVideoTrack = useCallback(async (newTrack) => {
    if (!roomRef.current || !newTrack) return false;
    if (localVideoTrackRef.current === newTrack) {
      return true;
    }

    try {
      // Unpublish old track
      if (localVideoTrackRef.current) {
        await roomRef.current.localParticipant.unpublishTrack(localVideoTrackRef.current);
        localVideoTrackRef.current.stop();
      }

      // Publish new track
      await roomRef.current.localParticipant.publishTrack(newTrack, {
        name: "camera",
        simulcast: true,
        videoCodec: "vp8",
      });
      localVideoTrackRef.current = newTrack;
      console.log("[LiveKit] Video track replaced");
      return true;
    } catch (err) {
      console.error("[LiveKit] Failed to replace video track:", err);
      return false;
    }
  }, []);

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      if (roomRef.current) {
        roomRef.current.disconnect();
      }
    };
  }, []);

  return {
    isLive,
    isConnecting,
    error,
    viewerCount,
    participants,
    startBroadcast,
    stopBroadcast,
    toggleAudio,
    toggleVideo,
    replaceVideoTrack,
    sendData,
    room: roomRef.current,
  };
}

export default useLiveKit;
