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

type PlayerStatus = "idle" | "connecting" | "playing" | "error";

function withNoTrailingSlash(url: string) {
  return url.endsWith("/") ? url.slice(0, -1) : url;
}

async function waitForIceGatheringComplete(pc: RTCPeerConnection) {
  if (pc.iceGatheringState === "complete") return;
  await new Promise<void>((resolve) => {
    const onState = () => {
      if (pc.iceGatheringState === "complete") {
        pc.removeEventListener("icegatheringstatechange", onState);
        resolve();
      }
    };
    pc.addEventListener("icegatheringstatechange", onState);
  });
}

export function useWhepPlayer({
  enabled,
  webrtcBaseUrl,
  streamName,
}: {
  enabled: boolean;
  webrtcBaseUrl: string;
  streamName: string;
}) {
  const videoRef = useRef<HTMLVideoElement | null>(null);
  const pcRef = useRef<RTCPeerConnection | null>(null);
  const sessionUrlRef = useRef<string | null>(null);
  const abortRef = useRef<AbortController | null>(null);
  const [status, setStatus] = useState<PlayerStatus>("idle");
  const [error, setError] = useState<string | null>(null);
  const [restartNonce, setRestartNonce] = useState(0);

  const whepUrl = useMemo(() => {
    return `${withNoTrailingSlash(webrtcBaseUrl)}/${encodeURIComponent(streamName)}/whep`;
  }, [webrtcBaseUrl, streamName]);

  const stop = useCallback(async () => {
    abortRef.current?.abort();
    abortRef.current = null;

    const sessionUrl = sessionUrlRef.current;
    sessionUrlRef.current = null;

    const pc = pcRef.current;
    pcRef.current = null;

    if (pc) {
      try {
        pc.ontrack = null;
        pc.onconnectionstatechange = null;
        pc.oniceconnectionstatechange = null;
        pc.close();
      } catch {
        // Ignore teardown errors when peer connection is already closed.
      }
    }

    const v = videoRef.current;
    if (v) {
      try {
        v.srcObject = null;
        v.removeAttribute("src");
      } catch {
        // Ignore cleanup errors on detached video element.
      }
    }

    if (sessionUrl) {
      try {
        await fetch(sessionUrl, { method: "DELETE" });
      } catch {
        // Ignore best-effort WHEP session cleanup failures.
      }
    }
  }, []);

  const restart = useCallback(() => {
    setRestartNonce((n) => n + 1);
  }, []);

  useEffect(() => {
    let disposed = false;

    const run = async () => {
      await stop();
      if (!enabled) {
        setStatus("idle");
        setError(null);
        return;
      }

      setStatus("connecting");
      setError(null);

      const abort = new AbortController();
      abortRef.current = abort;

      try {
        const videoEl = videoRef.current;
        if (!videoEl) throw new Error("no_video");

        const pc = new RTCPeerConnection();
        pcRef.current = pc;

        pc.addTransceiver("video", { direction: "recvonly" });
        pc.addTransceiver("audio", { direction: "recvonly" });

        pc.ontrack = (ev) => {
          const stream = ev.streams?.[0];
          if (!stream) return;
          if (videoEl.srcObject !== stream) {
            videoEl.srcObject = stream;
            void videoEl.play().catch(() => undefined);
          }
        };

        pc.onconnectionstatechange = () => {
          if (pc.connectionState === "failed") {
            restart();
          }
        };

        const offer = await pc.createOffer();
        await pc.setLocalDescription(offer);
        await waitForIceGatheringComplete(pc);

        const sdpOffer = pc.localDescription?.sdp;
        if (!sdpOffer) throw new Error("no_offer");

        const res = await fetch(whepUrl, {
          method: "POST",
          headers: {
            "Content-Type": "application/sdp",
            Accept: "application/sdp",
          },
          body: sdpOffer,
          signal: abort.signal,
        });

        if (!res.ok) {
          const t = await res.text().catch(() => "");
          if (res.status === 405) {
            throw new Error(
              "whep_http_405: endpoint WebRTC khong cho phep POST. Kiem tra mediamtx_webrtc_url (host/port) va nginx proxy."
            );
          }
          throw new Error(`whep_http_${res.status}${t ? `: ${t}` : ""}`);
        }

        const answerSdp = await res.text();
        const location = res.headers.get("location") || res.headers.get("Location");
        if (location) {
          try {
            const baseUrl = new URL(whepUrl);
            if (location.startsWith("/")) {
              // MediaMTX can return absolute paths like "/cam1/whep/<id>".
              // If WHEP is behind a prefix (e.g. "/mtx"), preserve that prefix for DELETE.
              const parts = baseUrl.pathname.split("/").filter(Boolean);
              const prefix = parts.slice(0, -2).join("/");
              const normalized = prefix ? `/${prefix}${location}` : location;
              sessionUrlRef.current = new URL(normalized, baseUrl.origin).toString();
            } else {
              sessionUrlRef.current = new URL(location, whepUrl).toString();
            }
          } catch {
            sessionUrlRef.current = location;
          }
        }

        await pc.setRemoteDescription({ type: "answer", sdp: answerSdp });
        if (!disposed) setStatus("playing");
      } catch (e) {
        if (!disposed) {
          setStatus("error");
          setError(e instanceof Error ? e.message : "unknown_error");
        }
      }
    };

    run();
    return () => {
      disposed = true;
      void stop();
    };
  }, [enabled, restartNonce, stop, whepUrl, restart]);

  return {
    videoRef,
    status,
    error,
    restart,
    stop,
  };
}

