"use client"

/**
 * CallModal — Appel audio/vidéo natif WebRTC.
 * Aucune dépendance externe : RTCPeerConnection + STUN publics.
 * Signaling par polling HTTP (cohérent avec l'architecture existante).
 */

import { useCallback, useEffect, useRef, useState } from "react"
import { PhoneOff, Mic, MicOff, Video, VideoOff, Phone, X } from "lucide-react"
import type { CallSession } from "@/src/lib/api/calls"
import { getCallSignal, patchCallSignal } from "@/src/lib/api/calls"

// Public STUN servers (Google + Cloudflare)
const ICE_SERVERS: RTCIceServer[] = [
  { urls: "stun:stun.l.google.com:19302" },
  { urls: "stun:stun1.l.google.com:19302" },
  { urls: "stun:stun.cloudflare.com:3478" },
]

type RtcState = "idle" | "connecting" | "connected" | "failed" | "ended"

// ── CallModal ─────────────────────────────────────────────────────────────────

type Props = {
  call: CallSession
  mySide: "tenant" | "customer"
  displayName: string
  onEnd: () => void
}

export function CallModal({ call, mySide, displayName, onEnd }: Props) {
  const isInitiator = call.initiator_side === mySide

  const [rtcState, setRtcState] = useState<RtcState>("idle")
  const [mutedAudio, setMutedAudio] = useState(false)
  const [mutedVideo, setMutedVideo] = useState(call.call_type === "audio")
  const [duration, setDuration] = useState(0)
  const [hasRemote, setHasRemote] = useState(false)
  const [statusMsg, setStatusMsg] = useState("Initialisation…")

  const localVideoRef  = useRef<HTMLVideoElement>(null)
  const remoteVideoRef = useRef<HTMLVideoElement>(null)
  const pcRef          = useRef<RTCPeerConnection | null>(null)
  const localStreamRef = useRef<MediaStream | null>(null)
  const pollRef        = useRef<ReturnType<typeof setInterval> | null>(null)
  const durationRef    = useRef<ReturnType<typeof setInterval> | null>(null)
  const iceFlushRef    = useRef<ReturnType<typeof setTimeout> | null>(null)
  const iceBufRef      = useRef<RTCIceCandidateInit[]>([])
  const addedIceRef    = useRef<Set<string>>(new Set())
  const mountedRef     = useRef(true)

  function stopPoll() {
    if (pollRef.current) clearInterval(pollRef.current)
    pollRef.current = null
  }

  function cleanup() {
    mountedRef.current = false
    stopPoll()
    if (durationRef.current) clearInterval(durationRef.current)
    if (iceFlushRef.current) clearTimeout(iceFlushRef.current)
    localStreamRef.current?.getTracks().forEach(t => t.stop())
    pcRef.current?.close()
    pcRef.current = null
    localStreamRef.current = null
  }

  // ── ICE candidate flush ────────────────────────────────────────────────────

  const flushIce = useCallback(() => {
    const batch = [...iceBufRef.current]
    iceBufRef.current = []
    if (!batch.length || !mountedRef.current) return
    patchCallSignal(call, mySide, isInitiator
      ? { add_ice_initiator: batch }
      : { add_ice_peer: batch }
    ).catch(() => {})
  }, [call, mySide, isInitiator])

  // ── Connection state → UI ──────────────────────────────────────────────────

  function handleConnState(pc: RTCPeerConnection) {
    pc.onconnectionstatechange = () => {
      if (!mountedRef.current) return
      const s = pc.connectionState
      if (s === "connected") {
        setRtcState("connected")
        setStatusMsg("Connecté")
        durationRef.current = setInterval(() => {
          if (mountedRef.current) setDuration(d => d + 1)
        }, 1000)
      } else if (s === "failed") {
        setRtcState("failed")
        setStatusMsg("Connexion échouée — vérifiez votre réseau")
      } else if (s === "disconnected") {
        setStatusMsg("Déconnecté…")
      }
    }
  }

  // ── Polling (shared) ───────────────────────────────────────────────────────

  function startPolling(pc: RTCPeerConnection, waitingFor: "answer" | "ice") {
    stopPoll()
    pollRef.current = setInterval(async () => {
      if (!mountedRef.current) return
      try {
        const sig = await getCallSignal(call, mySide)
        if (!sig || sig.status === "ended") {
          stopPoll()
          if (mountedRef.current) {
            setRtcState("ended")
            onEnd()
          }
          return
        }

        // Set remote description (initiator waits for answer)
        if (waitingFor === "answer" && sig.answer_sdp && !pc.remoteDescription) {
          await pc.setRemoteDescription(JSON.parse(sig.answer_sdp))
          if (mountedRef.current) setStatusMsg("Négociation…")
        }

        // Add ICE candidates from the other side
        const candidates = isInitiator ? sig.ice_peer : sig.ice_initiator
        for (const c of candidates ?? []) {
          const key = JSON.stringify(c)
          if (!addedIceRef.current.has(key)) {
            addedIceRef.current.add(key)
            await pc.addIceCandidate(c).catch(() => {})
          }
        }
      } catch {}
    }, 1500)
  }

  // ── Initiator flow ─────────────────────────────────────────────────────────

  async function runInitiator(pc: RTCPeerConnection) {
    setStatusMsg("Création de l'offre…")
    const offer = await pc.createOffer()
    await pc.setLocalDescription(offer)
    await patchCallSignal(call, mySide, { offer_sdp: JSON.stringify(offer) })
    if (mountedRef.current) setStatusMsg("En attente de l'autre participant…")
    startPolling(pc, "answer")
  }

  // ── Peer (callee) flow ─────────────────────────────────────────────────────

  async function waitForOfferAndAnswer(pc: RTCPeerConnection, attempt = 0) {
    if (!mountedRef.current) return
    if (attempt > 40) {   // 40 × 1.5s = 60s timeout
      setRtcState("failed")
      setStatusMsg("Aucune offre reçue — délai dépassé")
      return
    }
    try {
      const sig = await getCallSignal(call, mySide)
      if (!sig || sig.status === "ended") {
        setRtcState("ended"); onEnd(); return
      }
      if (!sig.offer_sdp) {
        setTimeout(() => waitForOfferAndAnswer(pc, attempt + 1), 1500)
        return
      }
      await pc.setRemoteDescription(JSON.parse(sig.offer_sdp))
      const answer = await pc.createAnswer()
      await pc.setLocalDescription(answer)
      await patchCallSignal(call, mySide, { answer_sdp: JSON.stringify(answer) })
      if (mountedRef.current) setStatusMsg("Réponse envoyée, connexion…")
      startPolling(pc, "ice")
    } catch (err) {
      setRtcState("failed")
      setStatusMsg("Erreur lors de la connexion")
    }
  }

  // ── Main setup ─────────────────────────────────────────────────────────────

  useEffect(() => {
    mountedRef.current = true

    async function startCall() {
      setRtcState("connecting")
      setStatusMsg("Accès microphone/caméra…")

      let stream: MediaStream
      try {
        stream = await navigator.mediaDevices.getUserMedia({
          audio: true,
          video: call.call_type === "video",
        })
      } catch {
        if (mountedRef.current) {
          setRtcState("failed")
          setStatusMsg("Impossible d'accéder au micro/caméra.")
        }
        return
      }

      if (!mountedRef.current) { stream.getTracks().forEach(t => t.stop()); return }

      localStreamRef.current = stream
      if (localVideoRef.current) localVideoRef.current.srcObject = stream

      const pc = new RTCPeerConnection({ iceServers: ICE_SERVERS })
      pcRef.current = pc

      stream.getTracks().forEach(track => pc.addTrack(track, stream))

      pc.ontrack = (event) => {
        if (!mountedRef.current) return
        if (remoteVideoRef.current) remoteVideoRef.current.srcObject = event.streams[0]
        setHasRemote(true)
      }

      handleConnState(pc)

      // Collect and batch-send ICE candidates
      pc.onicecandidate = (event) => {
        if (event.candidate) {
          iceBufRef.current.push(event.candidate.toJSON())
          if (iceFlushRef.current) clearTimeout(iceFlushRef.current)
          iceFlushRef.current = setTimeout(flushIce, 400)
        } else {
          // Gathering complete — flush immediately
          if (iceFlushRef.current) clearTimeout(iceFlushRef.current)
          flushIce()
        }
      }

      if (isInitiator) {
        await runInitiator(pc)
      } else {
        setStatusMsg("Récupération de l'offre…")
        await waitForOfferAndAnswer(pc)
      }
    }

    startCall().catch(() => {
      if (mountedRef.current) {
        setRtcState("failed")
        setStatusMsg("Erreur inattendue")
      }
    })

    return cleanup
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  // ── Controls ───────────────────────────────────────────────────────────────

  function toggleAudio() {
    const t = localStreamRef.current?.getAudioTracks()[0]
    if (!t) return
    t.enabled = !t.enabled
    setMutedAudio(!t.enabled)
  }

  function toggleVideo() {
    const t = localStreamRef.current?.getVideoTracks()[0]
    if (!t) return
    t.enabled = !t.enabled
    setMutedVideo(!t.enabled)
  }

  function handleEnd() {
    cleanup()
    onEnd()
  }

  function fmt(s: number) {
    return `${Math.floor(s / 60)}:${(s % 60).toString().padStart(2, "0")}`
  }

  const isVideo = call.call_type === "video"

  return (
    <div className="fixed inset-0 z-50 flex flex-col select-none" style={{ background: "#0a0f1e" }}>

      {/* Remote video — full background */}
      <video
        ref={remoteVideoRef}
        autoPlay
        playsInline
        className="absolute inset-0 h-full w-full object-cover"
        style={{ display: isVideo && hasRemote && !mutedVideo ? "block" : "none" }}
      />

      {/* Audio / waiting overlay */}
      {(!isVideo || !hasRemote) && (
        <div className="absolute inset-0 flex flex-col items-center justify-center gap-6">
          {/* Animated ring */}
          <div className="relative flex items-center justify-center">
            <div className="absolute h-36 w-36 rounded-full border-2 border-teal-500/30 animate-ping" />
            <div className="absolute h-28 w-28 rounded-full border border-teal-500/20 animate-ping [animation-delay:0.5s]" />
            <div className="h-24 w-24 rounded-full flex items-center justify-center"
              style={{ background: "linear-gradient(135deg, #1f7560 0%, #3aaa87 100%)" }}>
              {isVideo
                ? <Video className="h-10 w-10 text-white" />
                : <Phone className="h-10 w-10 text-white" />}
            </div>
          </div>
          <div className="text-center">
            <div className="text-white text-lg font-semibold">{call.initiator_name}</div>
            <div className="text-white/50 text-sm mt-1">
              {rtcState === "connected"
                ? fmt(duration)
                : statusMsg}
            </div>
          </div>
        </div>
      )}

      {/* Top bar */}
      <div className="relative z-10 shrink-0 flex items-center justify-between px-5 py-3"
        style={{ background: "linear-gradient(to bottom, rgba(0,0,0,0.65), transparent)" }}>
        <div className="flex items-center gap-3">
          {isVideo
            ? <Video className="h-4 w-4 text-teal-400" />
            : <Phone className="h-4 w-4 text-teal-400" />}
          <div>
            <div className="text-sm font-semibold text-white">
              {isVideo ? "Appel vidéo" : "Appel audio"}
            </div>
            <div className="text-xs text-white/50">
              {rtcState === "connected" ? fmt(duration) : statusMsg}
            </div>
          </div>
        </div>
        {/* Connection quality indicator */}
        <div className="flex items-center gap-1 text-[11px] text-white/30">
          <div className={`h-1.5 w-1.5 rounded-full ${
            rtcState === "connected" ? "bg-teal-400 animate-none" : "bg-amber-400 animate-pulse"
          }`} />
          WebRTC
        </div>
      </div>

      {/* Local video (picture-in-picture) */}
      {isVideo && (
        <div className="absolute top-16 right-4 z-20 w-32 h-[88px] rounded-2xl overflow-hidden border border-white/20 shadow-2xl bg-slate-900">
          <video ref={localVideoRef} autoPlay playsInline muted className="w-full h-full object-cover" />
          {mutedVideo && (
            <div className="absolute inset-0 flex items-center justify-center bg-slate-800/70">
              <VideoOff className="h-5 w-5 text-white/60" />
            </div>
          )}
        </div>
      )}

      {/* Audio local ref (no video) — attach even for audio calls so we don't output ourselves */}
      {!isVideo && <video ref={localVideoRef} autoPlay playsInline muted className="hidden" />}

      {/* Failed state */}
      {rtcState === "failed" && (
        <div className="absolute inset-x-0 top-1/2 -translate-y-1/2 z-20 flex flex-col items-center gap-4 px-8">
          <div className="rounded-2xl border border-red-500/30 bg-red-950/60 px-6 py-5 text-center max-w-sm w-full">
            <p className="text-white font-semibold">Connexion échouée</p>
            <p className="text-white/50 text-sm mt-1">{statusMsg}</p>
          </div>
        </div>
      )}

      {/* Bottom controls */}
      <div className="absolute bottom-0 inset-x-0 z-10 pb-10 pt-6 flex items-center justify-center gap-5"
        style={{ background: "linear-gradient(to top, rgba(0,0,0,0.65), transparent)" }}>

        {/* Mute mic */}
        <button
          onClick={toggleAudio}
          className="h-14 w-14 rounded-full flex items-center justify-center transition-all hover:scale-105 active:scale-95"
          style={{ background: mutedAudio ? "rgba(239,68,68,0.35)" : "rgba(255,255,255,0.18)" }}
          title={mutedAudio ? "Activer le micro" : "Couper le micro"}
        >
          {mutedAudio
            ? <MicOff className="h-6 w-6 text-red-400" />
            : <Mic className="h-6 w-6 text-white" />}
        </button>

        {/* Hang up */}
        <button
          onClick={handleEnd}
          className="h-16 w-16 rounded-full flex items-center justify-center shadow-xl transition-all hover:scale-105 active:scale-95"
          style={{ background: "#dc2626" }}
          title="Raccrocher"
        >
          <PhoneOff className="h-7 w-7 text-white" />
        </button>

        {/* Toggle camera (video only) */}
        {isVideo && (
          <button
            onClick={toggleVideo}
            className="h-14 w-14 rounded-full flex items-center justify-center transition-all hover:scale-105 active:scale-95"
            style={{ background: mutedVideo ? "rgba(239,68,68,0.35)" : "rgba(255,255,255,0.18)" }}
            title={mutedVideo ? "Activer la caméra" : "Couper la caméra"}
          >
            {mutedVideo
              ? <VideoOff className="h-6 w-6 text-red-400" />
              : <Video className="h-6 w-6 text-white" />}
          </button>
        )}
      </div>
    </div>
  )
}

// ── IncomingCallBanner ────────────────────────────────────────────────────────

type BannerProps = {
  call: CallSession
  onJoin: () => void
  onDecline: () => void
}

export function IncomingCallBanner({ call, onJoin, onDecline }: BannerProps) {
  return (
    <div
      className="mx-4 mb-2 flex items-center gap-3 rounded-2xl px-4 py-3 text-white shadow-lg"
      style={{ background: "linear-gradient(90deg, #1a3578 0%, #2655b0 100%)" }}
    >
      <div className="relative flex h-9 w-9 shrink-0 items-center justify-center rounded-full bg-teal-500">
        <span className="absolute inset-0 rounded-full bg-teal-500 animate-ping opacity-60" />
        {call.call_type === "audio"
          ? <Phone className="relative h-4 w-4" />
          : <Video className="relative h-4 w-4" />}
      </div>
      <div className="flex-1 min-w-0">
        <div className="text-sm font-semibold">
          {call.call_type === "audio" ? "Appel audio" : "Appel vidéo"}
        </div>
        <div className="text-xs text-white/60 truncate">
          {call.initiator_name} vous appelle…
        </div>
      </div>
      <button
        onClick={onJoin}
        className="rounded-xl bg-teal-500 hover:bg-teal-400 px-3 py-1.5 text-xs font-bold transition"
      >
        Décrocher
      </button>
      <button
        onClick={onDecline}
        className="rounded-xl border border-white/20 hover:bg-white/10 p-1.5 transition"
        title="Ignorer"
      >
        <X className="h-4 w-4" />
      </button>
    </div>
  )
}
