'use client';
import 'dotenv/config';
import React, { useEffect, useRef, useState } from 'react';
import {
  Room,
  connect,
  createLocalAudioTrack,
  RemoteTrack,
  Track,
  RemoteParticipant,
  LocalAudioTrack,
  DataPacket_Kind,
} from 'livekit-client';

type ChatMessage = { id: string; sender: string; text: string; ts: number };
type Transcript = { id: string; participant: string; text: string; ts: number };

export default function InterviewRealtime() {
  const [token, setToken] = useState<string | null>(null);
  const [roomName] = useState(process.env.NEXT_PUBLIC_AGENT_ROOM_NAME || 'interview-room');
  const [connecting, setConnecting] = useState(false);
  const roomRef = useRef<Room | null>(null);
  const localTrackRef = useRef<LocalAudioTrack | null>(null);

  const [participants, setParticipants] = useState<Record<string, { identity: string; connected: boolean }>>({});
  const [chat, setChat] = useState<ChatMessage[]>([]);
  const [transcripts, setTranscripts] = useState<Transcript[]>([]);

  // fetch token
  useEffect(() => {
    fetch(`/api/token?room=${roomName}&name=candidate`)
      .then((r) => r.json())
      .then((d) => {
        if (d?.token && typeof d.token === 'string') setToken(d.token);
      })
      .catch((e) => console.error('token fetch failed', e));
  }, [roomName]);

  // connect when token available
  useEffect(() => {
    if (!token) return;
    // console.log("🚀 ~ useEffect ~ process.env.LIVEKIT_URL:", process.env.NEXT_PUBLIC_LIVEKIT_URL)
    // if (!process.env.LIVEKIT_URL || typeof process.env.LIVEKIT_URL !== 'string') {
    //   console.error('NEXT_PUBLIC_LIVEKIT_URL is not set');
    //   return;
    // }

    let mounted = true;
    setConnecting(true);

    (async () => {
      try {

        const room = new Room();
        roomRef.current = room;

        // connect is instance method
        await room.connect("wss://vertex-ai-1879pkoh.livekit.cloud", token);

        if (!mounted) {
          room.disconnect();
          return;
        }

        roomRef.current = room;

        // initial participant list
        const updateParticipants = () => {
            if (!room || !room.participants) return;
            const map: Record<string, { identity: string; connected: boolean }> = {};
            // room.participants is a Map-like collection — use for..of for safety
            for (const p of room.participants.values()) {
              map[p.sid] = { identity: p.identity, connected: !p.isDisconnected };
            }
            if (room.localParticipant) {
              map[room.localParticipant.sid] = { identity: room.localParticipant.identity, connected: true };
            }
            setParticipants(map);
          };
        updateParticipants();

        room.on('participantConnected', updateParticipants);
        room.on('participantDisconnected', updateParticipants);
        room.on('participantUpdated', updateParticipants);

        // subscribe to remote tracks and participant events
        room.on('trackSubscribed', (track: RemoteTrack, publication, participant: RemoteParticipant) => {
          console.log("🚀 ~ InterviewRealtime ~ track:", track)
          if (track.kind === 'audio') {
            const el = track.attach();
            // guard attach return
            if (el && typeof (el.play) === 'function') {
              el.play().catch(() => {});
            }
          }
        });

        
        room.on('dataReceived', (payload: ArrayBuffer | Uint8Array, participant) => {
            try {
            const bytes = payload instanceof Uint8Array ? payload : new Uint8Array(payload);
            const text = new TextDecoder().decode(bytes);
            const parsed = JSON.parse(text);
        
            // normalize sender identity
            const sender = participant?.identity || parsed.from || 'agent';
        
            if (parsed.type === 'chat' && parsed.text) {
                setChat((c) => [{ id: String(Date.now()), sender, text: parsed.text, ts: Date.now() }, ...c]);
            } else if (parsed.type === 'transcript' && parsed.text) {
                setTranscripts((t) => [{ id: String(Date.now()), participant: sender, text: parsed.text, ts: Date.now() }, ...t]);
            } else {
                console.warn('unknown data message', parsed);
            }
            } catch (err) {
            console.warn('failed to parse data message', err);
            }
        });

        // publish local mic
        try {
          const audioTrack = await createLocalAudioTrack();
          localTrackRef.current = audioTrack;
          await room.localParticipant.publishTrack(audioTrack);
        } catch (err) {
          console.error('mic publish failed', err);
        }

        setConnecting(false);
      } catch (err) {
        console.error('connect failed', err);
        setConnecting(false);
      }
    })();

    return () => {
      mounted = false;
      // do not auto-disconnect here; leave explicit disconnect handler to user
    };
  }, [token]);

  // clean disconnect handler
  const handleDisconnect = async () => {
    const room = roomRef.current;
    if (!room) return;
    try {
      // stop local tracks
      room.localParticipant.tracks.forEach((pub) => {
        try {
          const t = pub.track as LocalAudioTrack | undefined;
          if (t && typeof (t.stop) === 'function') t.stop();
        } catch (e) {}
      });
      // unpublish local tracks
      await Promise.all(room.localParticipant.tracks.map((p) => p.unpublish()));
      // remove event listeners (best-effort)
      room.removeAllListeners();
      room.disconnect();
    } catch (err) {
      console.error('disconnect error', err);
    } finally {
      roomRef.current = null;
      localTrackRef.current = null;
      setParticipants({});
    }
  };

  // send chat or transcript as data message
  const sendDataMessage = (type: 'chat' | 'transcript', text: string) => {
    const room = roomRef.current;
    if (!room) return;
    const payload = JSON.stringify({ type, text });
    console.log("🚀 ~ sendDataMessage ~ payload:", payload)
    // reliable=true for chat, unreliable for transient transcription if desired
    room.localParticipant.publishData(Buffer.from(payload), { reliable: true, kind: DataPacket_Kind.RELIABLE });
    // mirror locally
    if (type === 'chat') {
      setChat((c) => [{ id: String(Date.now()), sender: room.localParticipant.identity, text, ts: Date.now() }, ...c]);
    } else {
      setTranscripts((t) => [{ id: String(Date.now()), participant: room.localParticipant.identity, text, ts: Date.now() }, ...t]);
    }
  };

  return (
    <div style={{ padding: 20 }}>
      <h2>Realtime Interview</h2>

      <div style={{ marginBottom: 12 }}>
        <strong>Room:</strong> {roomName} &nbsp;
        <button onClick={handleDisconnect} disabled={!roomRef.current}>
          Leave
        </button>
        {connecting && <span style={{ marginLeft: 8 }}>Connecting…</span>}
      </div>

      <div style={{ display: 'flex', gap: 20 }}>
        <div style={{ flex: 1, border: '1px solid #ddd', padding: 12 }}>
          <h3>Participants ({Object.keys(participants).length})</h3>
          <ul>
            {Object.entries(participants).map(([sid, p]) => (
              <li key={sid}>
                {p.identity} {p.connected ? '(online)' : '(offline)'}
              </li>
            ))}
          </ul>
        </div>

        <div style={{ flex: 2, border: '1px solid #ddd', padding: 12 }}>
          <h3>Chat</h3>
          <ChatBox onSend={(text) => sendDataMessage('chat', text)} messages={chat} />
        </div>

        <div style={{ flex: 3, border: '1px solid #ddd', padding: 12 }}>
          <h3>Live Transcripts</h3>
          <div style={{ maxHeight: 400, overflow: 'auto' }}>
            {transcripts.map((t) => (
              <div key={t.id} style={{ padding: 8, borderBottom: '1px solid #eee' }}>
                <small>{new Date(t.ts).toLocaleTimeString()}</small>
                <div><strong>{t.participant}</strong></div>
                <div>{t.text}</div>
              </div>
            ))}
          </div>
        </div>
      </div>
    </div>
  );
}

// Simple chat component (internal)
function ChatBox({ onSend, messages }: { onSend: (s: string) => void; messages: ChatMessage[] }) {
  const [input, setInput] = useState('');
  return (
    <div>
      <div style={{ maxHeight: 300, overflow: 'auto', marginBottom: 8 }}>
        {messages.map((m) => (
          <div key={m.id} style={{ padding: 6, borderBottom: '1px solid #f0f0f0' }}>
            <small>{new Date(m.ts).toLocaleTimeString()}</small>
            <div><strong>{m.sender}</strong> — {m.text}</div>
          </div>
        ))}
      </div>
      <div style={{ display: 'flex', gap: 8 }}>
        <input value={input} onChange={(e) => setInput(e.target.value)} style={{ flex: 1 }} />
        <button onClick={() => { if (input.trim()) { onSend(input.trim()); setInput(''); } }}>Send</button>
      </div>
    </div>
  );
}