import { useState, useCallback, useRef, useEffect } from "react";

const STREAM_SERVER = "https://stream.sociowire.com";
const API_BASE = (import.meta?.env?.VITE_API_BASE_URL || "").replace(/\/+$/, "");

// Remote debug logger - temporary
const remoteLog = async (level, ...args) => {
  const msg = args.map(a => typeof a === 'object' ? JSON.stringify(a) : String(a)).join(' ');
  console[level](...args);
  try {
    await fetch('/api/debug-log', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ level, msg, ts: new Date().toISOString(), src: 'broadcast' })
    }).catch(() => {});
  } catch {}
};
const ICE_SERVERS = [
  { urls: "stun:stun.l.google.com:19302" },
  {
    urls: "turns:turn.us2.sociowire.com:443",
    username: "sociowire",
    credential: "sociowire123",
  },
];

/**
 * Hook for WebRTC broadcasting with multi-participant support
 */
export function useWebRTCBroadcast({ streamID, userID, username, token }) {
  const [isLive, setIsLive] = useState(false);
  const [isConnecting, setIsConnecting] = useState(false);
  const [error, setError] = useState(null);
  const [viewerCount, setViewerCount] = useState(0);
  const [participants, setParticipants] = useState([]); // Other participants
  const [joinRequests, setJoinRequests] = useState([]); // Pending join requests

  const pcRef = useRef(null);
  const wsRef = useRef(null);
  const participantPCsRef = useRef(new Map()); // userID -> PeerConnection for each participant

  // Connect to WebSocket for real-time events
  const connectEventSocket = useCallback(() => {
    if (!streamID) return;

    const protocol = window.location.protocol === "https:" ? "wss" : "ws";
    const wsUrl = `${protocol}://stream.sociowire.com/api/streams/${streamID}/events`;

    try {
      const ws = new WebSocket(wsUrl);

      ws.onopen = () => {
        console.log("[Broadcast] Event socket connected");
        // Authenticate
        ws.send(JSON.stringify({ type: "auth", token, userID, role: "host" }));
      };

      ws.onmessage = (event) => {
        try {
          const msg = JSON.parse(event.data);
          handleEventMessage(msg);
        } catch (err) {
          console.error("[Broadcast] Failed to parse event:", err);
        }
      };

      ws.onclose = () => {
        console.log("[Broadcast] Event socket closed");
      };

      ws.onerror = (err) => {
        console.error("[Broadcast] Event socket error:", err);
      };

      wsRef.current = ws;
    } catch (err) {
      console.error("[Broadcast] Failed to connect event socket:", err);
    }
  }, [streamID, token, userID]);

  const handleEventMessage = useCallback((msg) => {
    switch (msg.type) {
      case "viewer_count":
        setViewerCount(msg.count || 0);
        break;

      case "join_request":
        setJoinRequests((prev) => [
          ...prev.filter((r) => r.userID !== msg.userID),
          { userID: msg.userID, username: msg.username, requestedAt: Date.now() },
        ]);
        break;

      case "join_request_cancelled":
        setJoinRequests((prev) => prev.filter((r) => r.userID !== msg.userID));
        break;

      case "participant_joined":
        // Another user joined as co-host, set up peer connection to receive their stream
        handleParticipantJoined(msg);
        break;

      case "participant_left":
        handleParticipantLeft(msg.userID);
        break;

      case "participant_offer":
        // Incoming offer from a participant
        handleParticipantOffer(msg);
        break;

      case "participant_ice":
        // ICE candidate from participant
        handleParticipantICE(msg);
        break;

      default:
        console.log("[Broadcast] Unknown event:", msg.type);
    }
  }, []);

  const handleParticipantJoined = useCallback(async (msg) => {
    const { userID: participantID, username: participantName } = msg;

    // Add to participants list
    setParticipants((prev) => [
      ...prev.filter((p) => p.userID !== participantID),
      { userID: participantID, username: participantName, stream: null },
    ]);

    // Remove from join requests
    setJoinRequests((prev) => prev.filter((r) => r.userID !== participantID));
  }, []);

  const handleParticipantLeft = useCallback((participantID) => {
    setParticipants((prev) => prev.filter((p) => p.userID !== participantID));

    // Close their peer connection
    const pc = participantPCsRef.current.get(participantID);
    if (pc) {
      pc.close();
      participantPCsRef.current.delete(participantID);
    }
  }, []);

  const handleParticipantOffer = useCallback(async (msg) => {
    const { userID: participantID, sdp } = msg;

    try {
      const pc = new RTCPeerConnection({ iceServers: ICE_SERVERS });

      pc.ontrack = (event) => {
        // Received participant's video/audio
        setParticipants((prev) =>
          prev.map((p) =>
            p.userID === participantID ? { ...p, stream: event.streams[0] } : p
          )
        );
      };

      pc.onicecandidate = (event) => {
        if (event.candidate && wsRef.current?.readyState === WebSocket.OPEN) {
          wsRef.current.send(
            JSON.stringify({
              type: "ice_candidate",
              targetUserID: participantID,
              candidate: event.candidate,
            })
          );
        }
      };

      await pc.setRemoteDescription({ type: "offer", sdp });
      const answer = await pc.createAnswer();
      await pc.setLocalDescription(answer);

      participantPCsRef.current.set(participantID, pc);

      // Send answer back via WebSocket
      if (wsRef.current?.readyState === WebSocket.OPEN) {
        wsRef.current.send(
          JSON.stringify({
            type: "participant_answer",
            targetUserID: participantID,
            sdp: answer.sdp,
          })
        );
      }
    } catch (err) {
      console.error("[Broadcast] Failed to handle participant offer:", err);
    }
  }, []);

  const handleParticipantICE = useCallback((msg) => {
    const { userID: participantID, candidate } = msg;
    const pc = participantPCsRef.current.get(participantID);
    if (pc && candidate) {
      pc.addIceCandidate(new RTCIceCandidate(candidate)).catch((err) => {
        console.error("[Broadcast] Failed to add ICE candidate:", err);
      });
    }
  }, []);

  // Start broadcasting
  const startBroadcast = useCallback(
    async (mainStream, selfStream, metadata = {}) => {
      // Synchronous log first to ensure it fires
      console.log("[Broadcast] startBroadcast ENTRY:", {
        hasMainStream: !!mainStream,
        hasSelfStream: !!selfStream,
        isConnecting,
        isLive
      });

      // Remote log (async but fire-and-forget)
      await remoteLog("log", "[Broadcast] startBroadcast called:", {
        hasMainStream: !!mainStream,
        hasSelfStream: !!selfStream,
        metadata,
        isConnecting,
        isLive
      });

      if (isConnecting || isLive) {
        remoteLog("warn", "[Broadcast] startBroadcast early return:", { isConnecting, isLive });
        return;
      }
      setIsConnecting(true);
      setError(null);

      try {
        // Create stream on server first
        remoteLog("log", "[Broadcast] Creating stream on server:", STREAM_SERVER);
        const createRes = await fetch(`${STREAM_SERVER}/api/streams`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            ...(token ? { Authorization: `Bearer ${token}` } : {}),
          },
          body: JSON.stringify({
            id: streamID,
            type: "webrtc",
            title: metadata.title || "Live Stream",
            lat: metadata.lat,
            lon: metadata.lon,
            broadcaster_id: userID,
            broadcaster_name: username,
          }),
        });

        if (!createRes.ok) {
          const errData = await createRes.json().catch(() => ({}));
          remoteLog("error", "[Broadcast] Failed to create stream:", errData);
          throw new Error(errData.error || "Failed to create stream");
        }

        remoteLog("log", "[Broadcast] Stream created on server, setting up WebRTC...");

        // Create peer connection
        const pc = new RTCPeerConnection({ iceServers: ICE_SERVERS });

        // Add main stream tracks (video + audio)
        if (mainStream) {
          mainStream.getTracks().forEach((track) => {
            pc.addTrack(track, mainStream);
          });
        }

        // Add self/presenter stream (video only, labeled)
        if (selfStream) {
          selfStream.getVideoTracks().forEach((track) => {
            const sender = pc.addTrack(track, selfStream);
            // The server will identify this as the presenter track by stream ID
          });
        }

        // Handle ICE candidates
        pc.onicecandidate = (event) => {
          if (event.candidate && wsRef.current?.readyState === WebSocket.OPEN) {
            wsRef.current.send(
              JSON.stringify({
                type: "ice_candidate",
                candidate: event.candidate,
              })
            );
          }
        };

        pc.onconnectionstatechange = () => {
          console.log("[Broadcast] Connection state:", pc.connectionState);
          if (pc.connectionState === "failed" || pc.connectionState === "disconnected") {
            setError("Connection lost");
            setIsLive(false);
          }
        };

        // Create and send offer
        const offer = await pc.createOffer();
        await pc.setLocalDescription(offer);

        const res = await fetch(
          `${STREAM_SERVER}/api/webrtc/broadcast/${streamID}`,
          {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
              ...(token ? { Authorization: `Bearer ${token}` } : {}),
            },
            body: JSON.stringify({
              sdp: offer.sdp,
              userID,
              username,
            }),
          }
        );

        if (!res.ok) {
          const errData = await res.json().catch(() => ({}));
          remoteLog("error", "[Broadcast] WebRTC broadcast failed:", errData);
          throw new Error(errData.error || "Failed to start broadcast");
        }

        const { sdp: answerSDP } = await res.json();
        await pc.setRemoteDescription({ type: "answer", sdp: answerSDP });

        pcRef.current = pc;
        setIsLive(true);

        // Connect event socket for real-time updates
        connectEventSocket();

        remoteLog("log", "[Broadcast] Started successfully, isLive=true");
      } catch (err) {
        remoteLog("error", "[Broadcast] Failed to start:", err.message);
        setError(err.message || "Failed to start broadcast");
      } finally {
        setIsConnecting(false);
      }
    },
    [streamID, userID, username, token, isConnecting, isLive, connectEventSocket]
  );

  // Store stream metadata for post creation
  const streamMetaRef = useRef(null);

  const setStreamMeta = useCallback((meta) => {
    streamMetaRef.current = meta;
  }, []);

  // Stop broadcasting
  const stopBroadcast = useCallback(async (saveAsPost = false) => {
    remoteLog("log", "[Broadcast] stopBroadcast called:", { saveAsPost, hasMeta: !!streamMetaRef.current });
    // Close peer connection
    if (pcRef.current) {
      pcRef.current.close();
      pcRef.current = null;
    }

    // Close participant connections
    participantPCsRef.current.forEach((pc) => pc.close());
    participantPCsRef.current.clear();

    // Close WebSocket
    if (wsRef.current) {
      wsRef.current.close();
      wsRef.current = null;
    }

    // Stop recording and get video URL
    let videoURL = "";
    try {
      const stopRes = await fetch(`${STREAM_SERVER}/api/webrtc/stop/${streamID}`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          ...(token ? { Authorization: `Bearer ${token}` } : {}),
        },
      });
      if (stopRes.ok) {
        const data = await stopRes.json();
        videoURL = data.video_url || "";
        remoteLog("log", "[Broadcast] Recording stopped, video URL:", videoURL);
      }
    } catch (err) {
      console.error("[Broadcast] Failed to stop recording:", err);
    }

    // If saveAsPost, create a post on the main backend
    if (saveAsPost && streamMetaRef.current) {
      const meta = streamMetaRef.current;
      try {
        remoteLog("log", "[Broadcast] Creating post with meta:", meta, "API_BASE:", API_BASE, "videoURL:", videoURL);
        const postData = {
          title: meta.title || "Live stream",
          snippet: `Live stream by ${username}`,
          category: meta.category || "NEWS",
          sub_category: meta.sub_category || "Local",
          lat: meta.lat,
          lon: meta.lon,
          author: username,
          source: "sociowire",
          visibility: "public",
          video_url: videoURL || undefined, // Include video URL if available
        };
        remoteLog("log", "[Broadcast] POST data:", postData);
        const res = await fetch(`${API_BASE}/api/post`, {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            ...(token ? { Authorization: `Bearer ${token}` } : {}),
          },
          body: JSON.stringify(postData),
        });
        if (res.ok) {
          const post = await res.json();
          remoteLog("log", "[Broadcast] Created post from live:", post?.id || post);
          // Dispatch event so MapView can add the post to the map immediately
          window.dispatchEvent(new CustomEvent('live-post-created', { detail: post }));
        } else {
          const errText = await res.text();
          remoteLog("error", "[Broadcast] Failed to create post:", res.status, errText);
        }
      } catch (err) {
        remoteLog("error", "[Broadcast] Error creating post:", err.message);
      }
    }

    setIsLive(false);
    setViewerCount(0);
    setParticipants([]);
    setJoinRequests([]);
    setError(null);
    streamMetaRef.current = null;

    console.log("[Broadcast] Stopped");
  }, [streamID, token, username]);

  // Approve join request
  const approveJoinRequest = useCallback(
    async (requestUserID) => {
      try {
        const res = await fetch(
          `${STREAM_SERVER}/api/streams/${streamID}/approve/${requestUserID}`,
          {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
              ...(token ? { Authorization: `Bearer ${token}` } : {}),
            },
          }
        );

        if (!res.ok) {
          throw new Error("Failed to approve join request");
        }

        // Will receive participant_joined event via WebSocket
      } catch (err) {
        console.error("[Broadcast] Failed to approve join:", err);
        setError(err.message);
      }
    },
    [streamID, token]
  );

  // Deny join request
  const denyJoinRequest = useCallback(
    async (requestUserID) => {
      try {
        await fetch(
          `${STREAM_SERVER}/api/streams/${streamID}/deny/${requestUserID}`,
          {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
              ...(token ? { Authorization: `Bearer ${token}` } : {}),
            },
          }
        );

        setJoinRequests((prev) => prev.filter((r) => r.userID !== requestUserID));
      } catch (err) {
        console.error("[Broadcast] Failed to deny join:", err);
      }
    },
    [streamID, token]
  );

  // Kick participant
  const kickParticipant = useCallback(
    async (participantID) => {
      try {
        await fetch(
          `${STREAM_SERVER}/api/streams/${streamID}/kick/${participantID}`,
          {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
              ...(token ? { Authorization: `Bearer ${token}` } : {}),
            },
          }
        );

        handleParticipantLeft(participantID);
      } catch (err) {
        console.error("[Broadcast] Failed to kick participant:", err);
      }
    },
    [streamID, token, handleParticipantLeft]
  );

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      if (isLive) {
        stopBroadcast(false);
      }
    };
  }, []);

  return {
    isLive,
    isConnecting,
    error,
    viewerCount,
    participants,
    joinRequests,
    startBroadcast,
    stopBroadcast,
    setStreamMeta,
    approveJoinRequest,
    denyJoinRequest,
    kickParticipant,
  };
}

export default useWebRTCBroadcast;
