"use client";

import { useCallback, useEffect, useRef, useState } from "react";
import { useRouter } from "next/navigation";
import { VideoTile } from "./VideoTile";
import { MEETING_SESSION_KEY, type StoredMeetingSession } from "./JoinForm";
import type { ClientToServerMessage, ParticipantSummary, ServerToClientMessage } from "@/lib/meeting/protocol";

const ICE_SERVERS: RTCIceServer[] = [{ urls: "stun:stun.l.google.com:19302" }];

type RemoteEntry = ParticipantSummary & { stream: MediaStream | null };

type Phase = "loading" | "connecting" | "connected" | "kicked" | "ended" | "error";

export function CallRoom({ slug }: { slug: string }) {
  const router = useRouter();
  const [session, setSession] = useState<StoredMeetingSession | null>(null);
  const [phase, setPhase] = useState<Phase>("loading");
  const [errorMessage, setErrorMessage] = useState<string | null>(null);
  const [remotes, setRemotes] = useState<Map<string, RemoteEntry>>(new Map());
  const [localStream, setLocalStream] = useState<MediaStream | null>(null);
  const [micOn, setMicOn] = useState(true);
  const [camOn, setCamOn] = useState(true);
  const [sharingScreen, setSharingScreen] = useState(false);
  const [showParticipants, setShowParticipants] = useState(false);

  const wsRef = useRef<WebSocket | null>(null);
  const pcsRef = useRef<Map<string, RTCPeerConnection>>(new Map());
  const localStreamRef = useRef<MediaStream | null>(null);
  const screenStreamRef = useRef<MediaStream | null>(null);
  const sessionRef = useRef<StoredMeetingSession | null>(null);

  const send = useCallback((message: ClientToServerMessage) => {
    if (wsRef.current?.readyState === WebSocket.OPEN) {
      wsRef.current.send(JSON.stringify(message));
    }
  }, []);

  const closePeer = useCallback((participantId: string) => {
    pcsRef.current.get(participantId)?.close();
    pcsRef.current.delete(participantId);
    setRemotes((prev) => {
      const next = new Map(prev);
      next.delete(participantId);
      return next;
    });
  }, []);

  const createPeerConnection = useCallback(
    (participantId: string, name: string, role: ParticipantSummary["role"], isInitiator: boolean) => {
      const pc = new RTCPeerConnection({ iceServers: ICE_SERVERS });
      pcsRef.current.set(participantId, pc);

      localStreamRef.current?.getTracks().forEach((track) => {
        pc.addTrack(track, localStreamRef.current!);
      });

      pc.ontrack = (event) => {
        setRemotes((prev) => {
          const next = new Map(prev);
          const existing = next.get(participantId);
          next.set(participantId, { participantId, name, role, stream: event.streams[0] ?? existing?.stream ?? null });
          return next;
        });
      };

      pc.onicecandidate = (event) => {
        if (event.candidate) {
          send({ type: "ice-candidate", to: participantId, payload: event.candidate.toJSON() });
        }
      };

      setRemotes((prev) => {
        const next = new Map(prev);
        next.set(participantId, { participantId, name, role, stream: next.get(participantId)?.stream ?? null });
        return next;
      });

      if (isInitiator) {
        pc.createOffer()
          .then((offer) => pc.setLocalDescription(offer).then(() => offer))
          .then((offer) => send({ type: "offer", to: participantId, payload: offer }))
          .catch(() => setErrorMessage("Verbindung zu einem Teilnehmer ist fehlgeschlagen."));
      }

      return pc;
    },
    [send],
  );

  const cleanup = useCallback(() => {
    wsRef.current?.close();
    wsRef.current = null;
    pcsRef.current.forEach((pc) => pc.close());
    pcsRef.current.clear();
    localStreamRef.current?.getTracks().forEach((t) => t.stop());
    localStreamRef.current = null;
    screenStreamRef.current?.getTracks().forEach((t) => t.stop());
    screenStreamRef.current = null;
  }, []);

  // Load session + acquire local media + open signaling connection. This
  // reads sessionStorage and opens external connections, so it belongs in an
  // effect; the early setState calls below are unavoidable for the
  // missing/invalid-session error paths (no server-renderable equivalent).
  useEffect(() => {
    const raw = sessionStorage.getItem(MEETING_SESSION_KEY);
    if (!raw) {
      // eslint-disable-next-line react-hooks/set-state-in-effect
      setPhase("error");
      setErrorMessage("Keine aktive Meeting-Sitzung gefunden.");
      return;
    }
    let parsed: StoredMeetingSession;
    try {
      parsed = JSON.parse(raw);
    } catch {
      setPhase("error");
      setErrorMessage("Keine aktive Meeting-Sitzung gefunden.");
      return;
    }
    if (parsed.eventSlug !== slug) {
      setPhase("error");
      setErrorMessage("Diese Sitzung gehört zu einem anderen Termin.");
      return;
    }
    setSession(parsed);
    sessionRef.current = parsed;
    setPhase("connecting");

    let cancelled = false;

    (async () => {
      try {
        const stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
        if (cancelled) {
          stream.getTracks().forEach((t) => t.stop());
          return;
        }
        localStreamRef.current = stream;
        setLocalStream(stream);
      } catch {
        // Continue without local media — participant can still watch/listen to others.
        setErrorMessage("Kamera/Mikrofon konnten nicht aktiviert werden. Du kannst trotzdem teilnehmen.");
      }

      if (cancelled) return;

      const protocol = window.location.protocol === "https:" ? "wss" : "ws";
      const ws = new WebSocket(`${protocol}://${window.location.host}/ws/signaling?token=${encodeURIComponent(parsed.token)}`);
      wsRef.current = ws;

      ws.onmessage = (event) => {
        const message: ServerToClientMessage = JSON.parse(event.data);
        handleServerMessage(message);
      };
      ws.onclose = () => {
        setPhase((p) => (p === "kicked" || p === "ended" ? p : "ended"));
      };
      ws.onerror = () => setErrorMessage("Verbindung zum Meeting-Server fehlgeschlagen.");
    })();

    function handleServerMessage(message: ServerToClientMessage) {
      const me = sessionRef.current;
      if (!me) return;

      switch (message.type) {
        case "welcome": {
          setPhase("connected");
          for (const p of message.participants) {
            createPeerConnection(p.participantId, p.name, p.role, true);
          }
          break;
        }
        case "peer-joined": {
          createPeerConnection(message.participantId, message.name, message.role, false);
          break;
        }
        case "peer-left": {
          closePeer(message.participantId);
          break;
        }
        case "offer": {
          const pc =
            pcsRef.current.get(message.from) ??
            createPeerConnection(message.from, remotesLookupName(message.from), "participant", false);
          pc.setRemoteDescription(new RTCSessionDescription(message.payload))
            .then(() => pc.createAnswer())
            .then((answer) => pc.setLocalDescription(answer).then(() => answer))
            .then((answer) => send({ type: "answer", to: message.from, payload: answer }))
            .catch(() => setErrorMessage("Verbindung zu einem Teilnehmer ist fehlgeschlagen."));
          break;
        }
        case "answer": {
          pcsRef.current.get(message.from)?.setRemoteDescription(new RTCSessionDescription(message.payload));
          break;
        }
        case "ice-candidate": {
          pcsRef.current.get(message.from)?.addIceCandidate(new RTCIceCandidate(message.payload)).catch(() => {});
          break;
        }
        case "kicked": {
          setPhase("kicked");
          cleanup();
          sessionStorage.removeItem(MEETING_SESSION_KEY);
          break;
        }
      }
    }

    function remotesLookupName(participantId: string): string {
      return remotes.get(participantId)?.name ?? "Teilnehmer:in";
    }

    return () => {
      cancelled = true;
      cleanup();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [slug]);

  function toggleMic() {
    localStreamRef.current?.getAudioTracks().forEach((t) => (t.enabled = !micOn));
    setMicOn((v) => !v);
  }

  function toggleCam() {
    localStreamRef.current?.getVideoTracks().forEach((t) => (t.enabled = !camOn));
    setCamOn((v) => !v);
  }

  async function toggleScreenShare() {
    if (sharingScreen) {
      stopScreenShare();
      return;
    }
    try {
      const screenStream = await navigator.mediaDevices.getDisplayMedia({ video: true });
      screenStreamRef.current = screenStream;
      const screenTrack = screenStream.getVideoTracks()[0];
      screenTrack.onended = () => stopScreenShare();

      pcsRef.current.forEach((pc) => {
        const sender = pc.getSenders().find((s) => s.track?.kind === "video");
        sender?.replaceTrack(screenTrack);
      });
      setSharingScreen(true);
    } catch {
      // User cancelled the share dialog — nothing to do.
    }
  }

  function stopScreenShare() {
    screenStreamRef.current?.getTracks().forEach((t) => t.stop());
    screenStreamRef.current = null;
    const cameraTrack = localStreamRef.current?.getVideoTracks()[0] ?? null;
    pcsRef.current.forEach((pc) => {
      const sender = pc.getSenders().find((s) => s.track?.kind === "video");
      sender?.replaceTrack(cameraTrack);
    });
    setSharingScreen(false);
  }

  function kickParticipant(participantId: string) {
    send({ type: "kick", targetParticipantId: participantId });
  }

  function leaveMeeting() {
    cleanup();
    sessionStorage.removeItem(MEETING_SESSION_KEY);
    router.push(`/termine/${slug}`);
  }

  if (phase === "loading" || phase === "connecting") {
    return (
      <div className="flex min-h-[70vh] items-center justify-center bg-neutral-900 text-white">
        <p className="text-sm text-white/70">Verbindung wird aufgebaut …</p>
      </div>
    );
  }

  if (phase === "error" || !session) {
    return (
      <div className="flex min-h-[70vh] flex-col items-center justify-center gap-4 bg-neutral-900 px-6 text-center text-white">
        <p className="text-lg">{errorMessage || "Dieses Meeting konnte nicht geöffnet werden."}</p>
        <a
          href={`/termine/${slug}/beitreten`}
          className="rounded-full bg-anouma-mauve-dark px-6 py-3 text-sm font-medium hover:bg-anouma-plum"
        >
          Zurück zur Beitrittsseite
        </a>
      </div>
    );
  }

  if (phase === "kicked") {
    return (
      <div className="flex min-h-[70vh] flex-col items-center justify-center gap-4 bg-neutral-900 px-6 text-center text-white">
        <p className="text-lg">Du wurdest vom Meeting entfernt.</p>
        <a
          href={`/termine/${slug}/beitreten`}
          className="rounded-full bg-anouma-mauve-dark px-6 py-3 text-sm font-medium hover:bg-anouma-plum"
        >
          Erneut beitreten
        </a>
      </div>
    );
  }

  if (phase === "ended") {
    return (
      <div className="flex min-h-[70vh] flex-col items-center justify-center gap-4 bg-neutral-900 px-6 text-center text-white">
        <p className="text-lg">Die Verbindung zum Meeting wurde beendet.</p>
        <a
          href={`/termine/${slug}`}
          className="rounded-full bg-anouma-mauve-dark px-6 py-3 text-sm font-medium hover:bg-anouma-plum"
        >
          Zurück zum Termin
        </a>
      </div>
    );
  }

  const isHost = session.role === "host";
  const remoteList = [...remotes.values()];

  return (
    <div className="flex min-h-[80vh] flex-col bg-neutral-900 text-white">
      <div className="flex items-center justify-between border-b border-white/10 px-5 py-3">
        <span className="text-sm font-medium">{session.eventTitle}</span>
        <button
          type="button"
          onClick={() => setShowParticipants((v) => !v)}
          className="rounded-full bg-white/10 px-4 py-1.5 text-xs font-medium hover:bg-white/20"
        >
          Teilnehmer ({remoteList.length + 1})
        </button>
      </div>

      {errorMessage && (
        <p className="bg-amber-900/50 px-5 py-2 text-center text-xs text-amber-100">{errorMessage}</p>
      )}

      <div className="flex flex-1">
        <div className="grid flex-1 auto-rows-fr grid-cols-1 gap-3 p-4 sm:grid-cols-2 lg:grid-cols-3">
          <VideoTile stream={localStream} name={session.name} isLocal isHost={isHost} muted={!micOn} videoOff={!camOn} />
          {remoteList.map((r) => (
            <VideoTile key={r.participantId} stream={r.stream} name={r.name} isHost={r.role === "host"} />
          ))}
        </div>

        {showParticipants && (
          <aside className="w-64 shrink-0 border-l border-white/10 p-4">
            <h2 className="mb-3 text-xs font-medium uppercase tracking-wide text-white/60">Teilnehmer</h2>
            <ul className="space-y-2">
              <li className="flex items-center justify-between text-sm">
                <span>● {session.name} (du)</span>
              </li>
              {remoteList.map((r) => (
                <li key={r.participantId} className="flex items-center justify-between text-sm">
                  <span>● {r.name}</span>
                  {isHost && (
                    <button
                      type="button"
                      onClick={() => kickParticipant(r.participantId)}
                      className="text-xs text-white/50 hover:text-red-300"
                    >
                      entfernen
                    </button>
                  )}
                </li>
              ))}
            </ul>
          </aside>
        )}
      </div>

      <div className="flex items-center justify-center gap-3 border-t border-white/10 px-5 py-4">
        <ControlButton active={micOn} onClick={toggleMic} label={micOn ? "Mikrofon aus" : "Mikrofon an"} icon="mic" />
        <ControlButton active={camOn} onClick={toggleCam} label={camOn ? "Kamera aus" : "Kamera an"} icon="cam" />
        {isHost && (
          <ControlButton
            active={sharingScreen}
            onClick={toggleScreenShare}
            label={sharingScreen ? "Bildschirmfreigabe beenden" : "Bildschirm teilen"}
            icon="screen"
          />
        )}
        <button
          type="button"
          onClick={leaveMeeting}
          className="rounded-full bg-red-700 px-6 py-3 text-sm font-medium text-white hover:bg-red-800"
        >
          Meeting verlassen
        </button>
      </div>
    </div>
  );
}

function ControlButton({
  active,
  onClick,
  label,
  icon,
}: {
  active: boolean;
  onClick: () => void;
  label: string;
  icon: "mic" | "cam" | "screen";
}) {
  return (
    <button
      type="button"
      onClick={onClick}
      title={label}
      aria-pressed={active}
      className={`flex h-12 w-12 items-center justify-center rounded-full transition-colors ${
        active ? "bg-white/15 hover:bg-white/25" : "bg-red-700 hover:bg-red-800"
      }`}
    >
      <span className="sr-only">{label}</span>
      <Icon name={icon} off={!active} />
    </button>
  );
}

function Icon({ name, off }: { name: "mic" | "cam" | "screen"; off: boolean }) {
  if (name === "mic") {
    return (
      <svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true">
        {off && <path d="M2 2l20 20" stroke="white" strokeWidth="1.8" strokeLinecap="round" />}
        <path
          d="M12 15a3 3 0 0 0 3-3V6a3 3 0 0 0-6 0v6a3 3 0 0 0 3 3Zm5-3a5 5 0 0 1-10 0M12 18v3"
          stroke="white"
          strokeWidth="1.8"
          strokeLinecap="round"
          strokeLinejoin="round"
        />
      </svg>
    );
  }
  if (name === "cam") {
    return (
      <svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true">
        {off && <path d="M2 2l20 20" stroke="white" strokeWidth="1.8" strokeLinecap="round" />}
        <rect x="2" y="6" width="14" height="12" rx="2" stroke="white" strokeWidth="1.8" />
        <path d="M16 10l6-3v10l-6-3" stroke="white" strokeWidth="1.8" strokeLinejoin="round" />
      </svg>
    );
  }
  return (
    <svg width="20" height="20" viewBox="0 0 24 24" fill="none" aria-hidden="true">
      <rect x="2" y="4" width="20" height="13" rx="2" stroke="white" strokeWidth="1.8" />
      <path d="M8 21h8M12 17v4" stroke="white" strokeWidth="1.8" strokeLinecap="round" />
    </svg>
  );
}
