// DIGIFY Online Meeting React Application
const { useState, useEffect, useRef, useCallback } = React;

// ============================================================================
// API Client
// ============================================================================

const API_BASE = window.location.origin;

const api = {
  // Auth
  signup: (email, password, name) =>
    fetch(`${API_BASE}/api/auth/signup`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password, name }),
    }).then((r) => r.json()),

  login: (email, password) =>
    fetch(`${API_BASE}/api/auth/login`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email, password }),
    }).then((r) => r.json()),

  getMe: (token) =>
    fetch(`${API_BASE}/api/auth/me`, {
      headers: { Authorization: `Bearer ${token}` },
    }).then((r) => r.json()),

  // Meetings
  createMeeting: (token, data) =>
    fetch(`${API_BASE}/api/meetings`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${token}`,
      },
      body: JSON.stringify(data),
    }).then((r) => r.json()),

  getMeetings: (token) =>
    fetch(`${API_BASE}/api/meetings`, {
      headers: { Authorization: `Bearer ${token}` },
    }).then((r) => r.json()),

  getMeeting: (meetingId) =>
    fetch(`${API_BASE}/api/meetings/${meetingId}`).then((r) => r.json()),

  updateMeeting: (token, meetingId, data) =>
    fetch(`${API_BASE}/api/meetings/${meetingId}`, {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${token}`,
      },
      body: JSON.stringify(data),
    }).then((r) => r.json()),

  deleteMeeting: (token, meetingId) =>
    fetch(`${API_BASE}/api/meetings/${meetingId}`, {
      method: 'DELETE',
      headers: { Authorization: `Bearer ${token}` },
    }).then((r) => r.json()),

  // Invites
  createInvite: (token, meetingId, email) =>
    fetch(`${API_BASE}/api/meetings/${meetingId}/invite`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${token}`,
      },
      body: JSON.stringify({ email }),
    }).then((r) => r.json()),

  getInvite: (token) =>
    fetch(`${API_BASE}/api/invites/${token}`).then((r) => r.json()),

  // Consents
  saveConsent: (meetingId, data) =>
    fetch(`${API_BASE}/api/meetings/${meetingId}/consents`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(data),
    }).then((r) => r.json()),

  getConsents: (meetingId) =>
    fetch(`${API_BASE}/api/meetings/${meetingId}/consents`).then((r) => r.json()),

  // Recordings
  uploadRecording: (meetingId, file, uploadedBy) => {
    const formData = new FormData();
    formData.append('recording', file);
    formData.append('uploadedBy', uploadedBy);
    return fetch(`${API_BASE}/api/meetings/${meetingId}/recordings`, {
      method: 'POST',
      body: formData,
    }).then((r) => r.json());
  },

  getRecordings: (meetingId) =>
    fetch(`${API_BASE}/api/meetings/${meetingId}/recordings`).then((r) => r.json()),

  // Transcripts
  getTranscripts: (meetingId) =>
    fetch(`${API_BASE}/api/meetings/${meetingId}/transcript`).then((r) => r.json()),

  // Admin APIs
  adminListUsers: (token, params = {}) => {
    const searchParams = new URLSearchParams();
    Object.entries(params).forEach(([key, value]) => {
      if (value !== undefined && value !== null && value !== '') {
        searchParams.set(key, value);
      }
    });
    const query = searchParams.toString();
    return fetch(`${API_BASE}/api/admin/users${query ? `?${query}` : ''}`, {
      headers: { Authorization: `Bearer ${token}` },
    }).then((r) => r.json());
  },

  adminCreateUser: (token, payload) =>
    fetch(`${API_BASE}/api/admin/users`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${token}`,
      },
      body: JSON.stringify(payload),
    }).then((r) => r.json()),

  adminUpdateUser: (token, userId, payload) =>
    fetch(`${API_BASE}/api/admin/users/${userId}`, {
      method: 'PUT',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${token}`,
      },
      body: JSON.stringify(payload),
    }).then((r) => r.json()),

  adminDeleteUser: (token, userId) =>
    fetch(`${API_BASE}/api/admin/users/${userId}`, {
      method: 'DELETE',
      headers: { Authorization: `Bearer ${token}` },
    }).then((r) => r.json()),

  adminBulkUsers: (token, payload) =>
    fetch(`${API_BASE}/api/admin/users/bulk`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${token}`,
      },
      body: JSON.stringify(payload),
    }).then((r) => r.json()),

  adminAnalyticsOverview: (token) =>
    fetch(`${API_BASE}/api/admin/analytics/overview`, {
      headers: { Authorization: `Bearer ${token}` },
    }).then((r) => r.json()),

  adminRevenue: (token, days = 90) =>
    fetch(`${API_BASE}/api/admin/analytics/revenue?days=${days}`, {
      headers: { Authorization: `Bearer ${token}` },
    }).then((r) => r.json()),

  adminActivity: (token, days = 30) =>
    fetch(`${API_BASE}/api/admin/analytics/activity?days=${days}`, {
      headers: { Authorization: `Bearer ${token}` },
    }).then((r) => r.json()),

  adminHealth: (token) =>
    fetch(`${API_BASE}/api/admin/health`, {
      headers: { Authorization: `Bearer ${token}` },
    }).then((r) => r.json()),

  adminAuditLogs: (token, limit = 200) =>
    fetch(`${API_BASE}/api/admin/audit-logs?limit=${limit}`, {
      headers: { Authorization: `Bearer ${token}` },
    }).then((r) => r.json()),

  adminResetPassword: (token, userId, payload) =>
    fetch(`${API_BASE}/api/admin/users/${userId}/reset-password`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        Authorization: `Bearer ${token}`,
      },
      body: JSON.stringify(payload),
    }).then((r) => r.json()),

  requestPasswordReset: (email) =>
    fetch(`${API_BASE}/api/auth/reset-request`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ email }),
    }).then((r) => r.json()),

  adminExport: (token, entity) =>
    fetch(`${API_BASE}/api/admin/export/${entity}`, {
      headers: { Authorization: `Bearer ${token}` },
    }).then((r) => r.text()),
};

window.cloudRTCApi = api;

// ============================================================================
// WebRTC Manager
// ============================================================================

class WebRTCManager {
  constructor(meetingId, participantId, displayName, onStream, onRemoveStream, onMessage) {
    this.meetingId = meetingId;
    this.participantId = participantId;
    this.displayName = displayName;
    this.onStream = onStream;
    this.onRemoveStream = onRemoveStream;
    this.onMessage = onMessage;
    this.localStream = null;
    this.screenStream = null;
    this.peerConnections = new Map();
    this.ws = null;
    this.iceServers = [
      { urls: 'stun:stun.l.google.com:19302' },
      { urls: 'stun:stun1.l.google.com:19302' },
    ];
  }

  async connect(userId, role) {
    const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
    const wsUrl = `${wsProtocol}//${window.location.host}/api/rooms/${this.meetingId}/ws?participantId=${this.participantId}&displayName=${encodeURIComponent(this.displayName)}&role=${role}&userId=${userId || ''}`;

    this.ws = new WebSocket(wsUrl);

    this.ws.onopen = () => {
      console.log('WebSocket connected');
    };

    this.ws.onmessage = async (event) => {
      const message = JSON.parse(event.data);
      await this.handleMessage(message);
    };

    this.ws.onerror = (error) => {
      console.error('WebSocket error:', error);
    };

    this.ws.onclose = () => {
      console.log('WebSocket closed');
      this.cleanup();
    };
  }

  async handleMessage(message) {
    console.log('Received message:', message.type);

    switch (message.type) {
      case 'welcome':
        this.onMessage(message);
        // Create offers for existing participants
        for (const participant of message.participants) {
          if (participant.id !== this.participantId) {
            await this.createOffer(participant.id);
          }
        }
        break;

      case 'signal':
        await this.handleSignal(message.fromParticipantId, message.signal);
        break;

      case 'participant-joined':
        this.onMessage(message);
        break;

      case 'participant-left':
        this.removePeer(message.participantId);
        this.onMessage(message);
        break;

      default:
        this.onMessage(message);
    }
  }

  async handleSignal(fromParticipantId, signal) {
    if (signal.type === 'offer') {
      await this.handleOffer(fromParticipantId, signal);
    } else if (signal.type === 'answer') {
      await this.handleAnswer(fromParticipantId, signal);
    } else if (signal.candidate) {
      await this.handleIceCandidate(fromParticipantId, signal);
    }
  }

  async createOffer(targetParticipantId) {
    const pc = this.createPeerConnection(targetParticipantId);
    const offer = await pc.createOffer();
    await pc.setLocalDescription(offer);

    this.sendSignal({
      targetParticipantId,
      signal: {
        type: 'offer',
        sdp: offer.sdp,
      },
    });
  }

  async handleOffer(fromParticipantId, signal) {
    const pc = this.createPeerConnection(fromParticipantId);
    await pc.setRemoteDescription(new RTCSessionDescription({ type: 'offer', sdp: signal.sdp }));

    const answer = await pc.createAnswer();
    await pc.setLocalDescription(answer);

    this.sendSignal({
      targetParticipantId: fromParticipantId,
      signal: {
        type: 'answer',
        sdp: answer.sdp,
      },
    });
  }

  async handleAnswer(fromParticipantId, signal) {
    const pc = this.peerConnections.get(fromParticipantId);
    if (pc) {
      await pc.setRemoteDescription(new RTCSessionDescription({ type: 'answer', sdp: signal.sdp }));
    }
  }

  async handleIceCandidate(fromParticipantId, signal) {
    const pc = this.peerConnections.get(fromParticipantId);
    if (pc && signal.candidate) {
      await pc.addIceCandidate(new RTCIceCandidate(signal.candidate));
    }
  }

  createPeerConnection(participantId) {
    if (this.peerConnections.has(participantId)) {
      return this.peerConnections.get(participantId);
    }

    const pc = new RTCPeerConnection({ iceServers: this.iceServers });

    // Add local stream tracks
    if (this.localStream) {
      this.localStream.getTracks().forEach((track) => {
        pc.addTrack(track, this.localStream);
      });
    }

    // Handle ICE candidates
    pc.onicecandidate = (event) => {
      if (event.candidate) {
        this.sendSignal({
          targetParticipantId: participantId,
          signal: {
            candidate: event.candidate,
          },
        });
      }
    };

    // Handle remote stream
    pc.ontrack = (event) => {
      this.onStream(participantId, event.streams[0]);
    };

    // Handle connection state
    pc.onconnectionstatechange = () => {
      if (pc.connectionState === 'disconnected' || pc.connectionState === 'failed') {
        this.removePeer(participantId);
      }
    };

    this.peerConnections.set(participantId, pc);
    return pc;
  }

  sendSignal(message) {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify({ type: 'signal', ...message }));
    }
  }

  sendMessage(message) {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      this.ws.send(JSON.stringify(message));
    }
  }

  async startLocalStream() {
    try {
      this.localStream = await navigator.mediaDevices.getUserMedia({
        video: true,
        audio: true,
      });
      return this.localStream;
    } catch (error) {
      console.error('Error accessing media devices:', error);
      throw error;
    }
  }

  async startScreenShare() {
    try {
      this.screenStream = await navigator.mediaDevices.getDisplayMedia({
        video: true,
        audio: true,
      });

      // Replace video track in all peer connections
      const videoTrack = this.screenStream.getVideoTracks()[0];
      this.peerConnections.forEach((pc) => {
        const sender = pc.getSenders().find((s) => s.track?.kind === 'video');
        if (sender) {
          sender.replaceTrack(videoTrack);
        }
      });

      // Handle screen share stop
      videoTrack.onended = () => {
        this.stopScreenShare();
      };

      this.sendMessage({ type: 'start-screenshare' });
      return this.screenStream;
    } catch (error) {
      console.error('Error starting screen share:', error);
      throw error;
    }
  }

  async stopScreenShare() {
    if (this.screenStream) {
      this.screenStream.getTracks().forEach((track) => track.stop());
      this.screenStream = null;

      // Restore camera video track
      if (this.localStream) {
        const videoTrack = this.localStream.getVideoTracks()[0];
        this.peerConnections.forEach((pc) => {
          const sender = pc.getSenders().find((s) => s.track?.kind === 'video');
          if (sender) {
            sender.replaceTrack(videoTrack);
          }
        });
      }

      this.sendMessage({ type: 'stop-screenshare' });
    }
  }

  toggleAudio(enabled) {
    if (this.localStream) {
      this.localStream.getAudioTracks().forEach((track) => {
        track.enabled = enabled;
      });
    }
  }

  toggleVideo(enabled) {
    if (this.localStream) {
      this.localStream.getVideoTracks().forEach((track) => {
        track.enabled = enabled;
      });
    }
  }

  removePeer(participantId) {
    const pc = this.peerConnections.get(participantId);
    if (pc) {
      pc.close();
      this.peerConnections.delete(participantId);
      this.onRemoveStream(participantId);
    }
  }

  cleanup() {
    if (this.localStream) {
      this.localStream.getTracks().forEach((track) => track.stop());
      this.localStream = null;
    }

    if (this.screenStream) {
      this.screenStream.getTracks().forEach((track) => track.stop());
      this.screenStream = null;
    }

    this.peerConnections.forEach((pc) => pc.close());
    this.peerConnections.clear();

    if (this.ws) {
      this.ws.close();
      this.ws = null;
    }
  }
}

// ============================================================================
// Components
// ============================================================================

// Auth Component
function Auth({ onLogin }) {
  const [isLogin, setIsLogin] = useState(true);
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');
  const [name, setName] = useState('');
  const [error, setError] = useState('');
  const [loading, setLoading] = useState(false);
  const [showResetModal, setShowResetModal] = useState(false);
  const [resetEmail, setResetEmail] = useState('');

  const handleSubmit = async (e) => {
    e.preventDefault();
    setError('');
    setLoading(true);

    try {
      const result = isLogin
        ? await api.login(email, password)
        : await api.signup(email, password, name);

      if (result.error) {
        setError(result.error);
      } else {
        localStorage.setItem('token', result.token);
        localStorage.setItem('user', JSON.stringify(result.user));
        onLogin(result.user, result.token);
      }
    } catch (err) {
      setError('An error occurred. Please try again.');
    } finally {
      setLoading(false);
    }
  };

  return (
    <div className="min-h-screen sketch-background">
      <div className="sketch-card w-full max-w-md p-8">
        <div className="flex flex-col items-center text-center gap-3 mb-6">
          <img
            src="/static/digify.png"
            alt="DIGIFY Online Meeting logo"
            className="sketch-brand-mark"
            loading="lazy"
          />
          <span className="sketch-tagline uppercase tracking-[0.35em] text-xs text-slate-600">
            Edge Orchestrated Rooms
          </span>
          <h1 className="sketch-hero-title text-center">DIGIFY Online Meeting</h1>
          <p className="sketch-subtext">
            Edge-native collaboration with AI transcription and semantic recall for Indonesian F&B growth teams.
          </p>
        </div>

        <form onSubmit={handleSubmit} className="space-y-5">
          {!isLogin && (
            <div>
              <label className="sketch-label">Name</label>
              <input
                type="text"
                value={name}
                onChange={(e) => setName(e.target.value)}
                className="sketch-input"
                required={!isLogin}
              />
            </div>
          )}

          <div>
            <label className="sketch-label">Email</label>
            <input
              type="email"
              value={email}
              onChange={(e) => setEmail(e.target.value)}
              className="sketch-input"
              required
            />
          </div>

          <div>
            <label className="sketch-label">Password</label>
            <input
              type="password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
              className="sketch-input"
              required
            />
          </div>

          {error && (
            <div className="sketch-alert sketch-alert--danger">
              <i className="fas fa-exclamation-triangle mr-2"></i>
              {error}
            </div>
          )}

          <button
            type="submit"
            disabled={loading}
            className="w-full sketch-button sketch-button--primary disabled:cursor-not-allowed disabled:opacity-70"
          >
            {loading ? 'Please wait...' : isLogin ? 'Sign In' : 'Sign Up'}
          </button>
        </form>

        <div className="sketch-divider mt-6"></div>
        <div className="mt-4 text-center text-sm">
          <button
            onClick={() => setIsLogin(!isLogin)}
            className="sketch-link"
          >
            {isLogin ? "Don't have an account? Sign up" : 'Already have an account? Sign in'}
          </button>
        </div>

        <div className="mt-2 text-center">
          <button
            type="button"
            className="sketch-link"
            onClick={() => { setShowResetModal(true); setResetEmail(email); }}
          >
            Forgot password?
          </button>
        </div>

        {showResetModal && (
          <div className="fixed inset-0 z-50 flex items-center justify-center p-6">
            <div className="w-full max-w-md bg-white rounded-lg shadow-lg p-6">
              <h3 className="text-lg font-semibold mb-2">Reset your password</h3>
              <p className="text-sm text-gray-600 mb-4">Enter your account email and we'll send a reset link if an account exists.</p>
              <div className="mb-4">
                <label className="block text-sm text-gray-700 mb-1">Email</label>
                <input
                  type="email"
                  value={resetEmail}
                  onChange={(e) => setResetEmail(e.target.value)}
                  className="w-full px-3 py-2 border rounded bg-gray-50"
                />
              </div>
              <div className="flex items-center justify-end space-x-2">
                <button
                  onClick={() => setShowResetModal(false)}
                  className="px-4 py-2 bg-gray-200 rounded"
                >
                  Close
                </button>
                <button
                  className="px-4 py-2 bg-blue-600 text-white rounded"
                  onClick={async () => {
                    try {
                      const result = await api.requestPasswordReset(resetEmail || email || '');
                      if (result?.mailto) {
                        // open mail client as fallback
                        window.location.href = result.mailto;
                      } else {
                        alert(result.message || 'If an account exists, a reset link will be sent');
                      }
                    } catch (err) {
                      alert('Failed to submit reset request');
                    } finally {
                      setShowResetModal(false);
                    }
                  }}
                >
                  Request reset
                </button>
              </div>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

// Dashboard Component
function Dashboard({ user, token, onLogout, onJoinMeeting }) {
  const [meetings, setMeetings] = useState([]);
  const [showCreateModal, setShowCreateModal] = useState(false);
  const [showMonitoring, setShowMonitoring] = useState(false);
  const [showAdmin, setShowAdmin] = useState(false);
  const [loading, setLoading] = useState(true);

  const isAdmin = user?.role === 'admin' || user?.role === 'superadmin';

  useEffect(() => {
    loadMeetings();
  }, []);

  const loadMeetings = async () => {
    try {
      const data = await api.getMeetings(token);
      // API may return an object with error or wrapper; ensure we set an array
      if (Array.isArray(data)) {
        setMeetings(data);
      } else if (data && Array.isArray(data.meetings)) {
        setMeetings(data.meetings);
      } else {
        setMeetings([]);
      }
    } catch (error) {
      console.error('Failed to load meetings:', error);
    } finally {
      setLoading(false);
    }
  };

  const handleCreateMeeting = async (meetingData) => {
    try {
      await api.createMeeting(token, meetingData);
      setShowCreateModal(false);
      loadMeetings();
    } catch (error) {
      console.error('Failed to create meeting:', error);
    }
  };

  return (
    <div className="min-h-screen sketch-surface">
      <nav className="sketch-toolbar sketch-toolbar--header px-6 py-4">
        <div className="max-w-7xl mx-auto flex flex-col md:flex-row md:justify-between md:items-center gap-4">
          <div className="flex items-center gap-3">
            <img
              src="/static/digify.png"
              alt="DIGIFY Online Meeting"
              className="sketch-brand-mark--sm"
              loading="lazy"
            />
            <div>
              <span className="sketch-tagline block">Edge orchestrated rooms</span>
              <h1 className="sketch-heading text-xl md:text-2xl">DIGIFY Online Meeting</h1>
            </div>
          </div>
          <div className="flex items-center flex-wrap gap-3">
            <span className="sketch-shadow-chip">
              <i className="fas fa-user text-emerald-500"></i>
              {user.name}
            </span>
            <button
              onClick={() => setShowMonitoring(true)}
              className="sketch-button sketch-button--secondary text-sm"
              title="System Monitoring"
            >
              <i className="fas fa-chart-line"></i>
              Monitoring
            </button>
            {isAdmin && window.AdminDashboard && (
              <button
                onClick={() => setShowAdmin(true)}
                className="sketch-button sketch-button--primary text-sm"
                title="Admin Dashboard"
              >
                <i className="fas fa-shield-alt"></i>
                Admin
              </button>
            )}
            <button
              onClick={onLogout}
              className="sketch-button sketch-button--danger text-sm"
            >
              <i className="fas fa-sign-out-alt"></i>
              Logout
            </button>
          </div>
        </div>
      </nav>

      <div className="max-w-7xl mx-auto px-6 py-10">
        <div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4 mb-8">
          <div>
            <h2 className="text-2xl font-bold sketch-heading">My Meetings</h2>
            <p className="text-sm text-slate-500">
              Coordinate live rooms, recordings, and transcripts from one control edge.
            </p>
          </div>
          <button
            onClick={() => setShowCreateModal(true)}
            className="sketch-button sketch-button--primary"
          >
            <i className="fas fa-plus"></i>
            Schedule Meeting
          </button>
        </div>

        {loading ? (
          <div className="text-center py-16">
            <i className="fas fa-spinner fa-spin text-4xl text-emerald-500"></i>
          </div>
        ) : meetings.length === 0 ? (
          <div className="sketch-empty-state text-center">
            <i className="fas fa-calendar-alt text-5xl text-emerald-400 mb-4"></i>
            <p className="text-xl font-semibold text-slate-600 mb-2">No meetings scheduled</p>
            <p className="text-slate-500">Create your first meeting to get started</p>
          </div>
        ) : (
          <div className="grid gap-6">
            {meetings.map((meeting) => (
              <MeetingCard
                key={meeting.id}
                meeting={meeting}
                token={token}
                onJoin={() => onJoinMeeting(meeting.id)}
                onUpdate={loadMeetings}
              />
            ))}
          </div>
        )}
      </div>

      {showCreateModal && (
        <CreateMeetingModal
          onClose={() => setShowCreateModal(false)}
          onCreate={handleCreateMeeting}
        />
      )}

      {showMonitoring && window.MonitoringDashboard && (
        <window.MonitoringDashboard
          token={token}
          user={user}
          onClose={() => setShowMonitoring(false)}
        />
      )}

      {showAdmin && window.AdminDashboard && (
        <window.AdminDashboard
          token={token}
          user={user}
          onClose={() => setShowAdmin(false)}
        />
      )}
    </div>
  );
}

// Meeting Card Component
function MeetingCard({ meeting, token, onJoin, onUpdate }) {
  const [showInviteModal, setShowInviteModal] = useState(false);
  const [inviteUrl, setInviteUrl] = useState('');

  const handleGenerateInvite = async () => {
    try {
      const result = await api.createInvite(token, meeting.id);
      setInviteUrl(result.inviteUrl);
      setShowInviteModal(true);
    } catch (error) {
      console.error('Failed to generate invite:', error);
    }
  };

  const copyInviteUrl = () => {
    navigator.clipboard.writeText(inviteUrl);
    alert('Invite link copied to clipboard!');
  };

  const startTime = new Date(meeting.start_time);
  const isUpcoming = startTime > new Date();

  return (
    <div className="sketch-panel">
      <div className="flex flex-col lg:flex-row lg:justify-between lg:items-start gap-4">
        <div className="flex-1 space-y-3">
          <h3 className="text-xl font-bold sketch-heading">{meeting.title}</h3>
          {meeting.description && (
            <p className="text-slate-500">{meeting.description}</p>
          )}
          <div className="flex flex-wrap items-center gap-3 text-sm text-slate-500">
            <span className="flex items-center gap-2">
              <i className="fas fa-calendar"></i>
              {startTime.toLocaleDateString()}
            </span>
            <span className="flex items-center gap-2">
              <i className="fas fa-clock"></i>
              {startTime.toLocaleTimeString()}
            </span>
            <span className={`sketch-badge ${isUpcoming ? 'sketch-badge--success' : 'sketch-badge--neutral'}`}>
              {meeting.status}
            </span>
          </div>
        </div>
        <div className="flex flex-wrap gap-2">
          <button
            onClick={handleGenerateInvite}
            className="sketch-button sketch-button--secondary text-sm"
            title="Generate invite link"
          >
            <i className="fas fa-link"></i>
            Invite
          </button>
          <button
            onClick={onJoin}
            className="sketch-button sketch-button--success text-sm"
          >
            <i className="fas fa-video"></i>
            Join
          </button>
        </div>
      </div>

      {showInviteModal && (
        <div className="fixed inset-0 sketch-overlay flex items-center justify-center z-50">
          <div className="sketch-modal p-6 max-w-md w-full">
            <h3 className="text-xl font-bold sketch-heading mb-4">Meeting Invite Link</h3>
            <div className="sketch-card p-4 mb-4 break-all text-sm">
              {inviteUrl}
            </div>
            <div className="flex flex-wrap gap-2">
              <button
                onClick={copyInviteUrl}
                className="flex-1 sketch-button sketch-button--secondary"
              >
                <i className="fas fa-copy"></i>
                Copy Link
              </button>
              <button
                onClick={() => setShowInviteModal(false)}
                className="sketch-button sketch-button--ghost"
              >
                Close
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// Create Meeting Modal Component
function CreateMeetingModal({ onClose, onCreate }) {
  const [title, setTitle] = useState('');
  const [description, setDescription] = useState('');
  const [startTime, setStartTime] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    onCreate({
      title,
      description,
      start_time: new Date(startTime).toISOString(),
      password: password || undefined,
      timezone: Intl.DateTimeFormat().resolvedOptions().timeZone,
    });
  };

  // Set default start time to 1 hour from now
  useEffect(() => {
    const now = new Date();
    now.setHours(now.getHours() + 1);
    const localDateTime = now.toISOString().slice(0, 16);
    setStartTime(localDateTime);
  }, []);

  return (
    <div className="fixed inset-0 sketch-overlay flex items-center justify-center z-50">
      <div className="sketch-modal p-6 max-w-lg w-full">
        <h3 className="text-xl font-bold sketch-heading mb-4">Schedule New Meeting</h3>
        <form onSubmit={handleSubmit} className="space-y-5">
          <div>
            <label className="sketch-label">Title *</label>
            <input
              type="text"
              value={title}
              onChange={(e) => setTitle(e.target.value)}
              className="sketch-input"
              required
            />
          </div>

          <div>
            <label className="sketch-label">Description</label>
            <textarea
              value={description}
              onChange={(e) => setDescription(e.target.value)}
              className="sketch-input sketch-textarea"
              rows="3"
            />
          </div>

          <div>
            <label className="sketch-label">Start Time *</label>
            <input
              type="datetime-local"
              value={startTime}
              onChange={(e) => setStartTime(e.target.value)}
              className="sketch-input"
              required
            />
          </div>

          <div>
            <label className="sketch-label">Password (optional)</label>
            <input
              type="password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
              className="sketch-input"
            />
          </div>

          <div className="flex flex-wrap gap-3">
            <button
              type="submit"
              className="flex-1 sketch-button sketch-button--primary"
            >
              Create Meeting
            </button>
            <button
              type="button"
              onClick={onClose}
              className="sketch-button sketch-button--ghost"
            >
              Cancel
            </button>
          </div>
        </form>
      </div>
    </div>
  );
}

// VIDEO ROOM COMPONENT - Part 1 (continuing in next section)
function VideoRoom({ meetingId, user, onLeave }) {
  const [participants, setParticipants] = useState(new Map());
  const [messages, setMessages] = useState([]);
  const [localStream, setLocalStream] = useState(null);
  const [audioEnabled, setAudioEnabled] = useState(true);
  const [videoEnabled, setVideoEnabled] = useState(true);
  const [screenSharing, setScreenSharing] = useState(false);
  const [recording, setRecording] = useState(false);
  const [showConsentModal, setShowConsentModal] = useState(false);
  const [recordingConsent, setRecordingConsent] = useState(false);
  const [mediaRecorder, setMediaRecorder] = useState(null);
  const [recordedChunks, setRecordedChunks] = useState([]);
  const [myRole, setMyRole] = useState('participant');
  
  const webrtcManager = useRef(null);
  const localVideoRef = useRef(null);
  const participantId = useRef(crypto.randomUUID());

  useEffect(() => {
    initializeRoom();
    return () => {
      if (webrtcManager.current) {
        webrtcManager.current.cleanup();
      }
    };
  }, []);

  const initializeRoom = async () => {
    try {
      // Initialize WebRTC manager
      const manager = new WebRTCManager(
        meetingId,
        participantId.current,
        user?.name || 'Guest',
        handleRemoteStream,
        handleRemoveStream,
        handleRoomMessage
      );

      webrtcManager.current = manager;

      // Start local stream
      const stream = await manager.startLocalStream();
      setLocalStream(stream);
      if (localVideoRef.current) {
        localVideoRef.current.srcObject = stream;
      }

      // Connect to room
      await manager.connect(user?.id, user ? 'host' : 'participant');
    } catch (error) {
      console.error('Failed to initialize room:', error);
      alert('Failed to access camera/microphone');
    }
  };

  const handleRemoteStream = (participantId, stream) => {
    setParticipants((prev) => {
      const newMap = new Map(prev);
      newMap.set(participantId, { id: participantId, stream });
      return newMap;
    });
  };

  const handleRemoveStream = (participantId) => {
    setParticipants((prev) => {
      const newMap = new Map(prev);
      newMap.delete(participantId);
      return newMap;
    });
  };

  const handleRoomMessage = (message) => {
    console.log('Room message:', message);
    
    if (message.type === 'welcome') {
      setMyRole(message.role);
    } else if (message.type === 'recording-started') {
      setShowConsentModal(true);
    } else if (message.type === 'recording-stopped') {
      setRecording(false);
      if (mediaRecorder && mediaRecorder.state !== 'inactive') {
        mediaRecorder.stop();
      }
    } else if (message.type === 'chat') {
      setMessages((prev) => [...prev, message]);
    }
  };

  const toggleAudio = () => {
    const enabled = !audioEnabled;
    setAudioEnabled(enabled);
    webrtcManager.current?.toggleAudio(enabled);
  };

  const toggleVideo = () => {
    const enabled = !videoEnabled;
    setVideoEnabled(enabled);
    webrtcManager.current?.toggleVideo(enabled);
  };

  const toggleScreenShare = async () => {
    try {
      if (screenSharing) {
        await webrtcManager.current?.stopScreenShare();
        setScreenSharing(false);
      } else {
        await webrtcManager.current?.startScreenShare();
        setScreenSharing(true);
      }
    } catch (error) {
      console.error('Screen share error:', error);
    }
  };

  const startRecording = () => {
    if (myRole === 'host' || myRole === 'co-host') {
      webrtcManager.current?.sendMessage({ type: 'start-recording' });
      setShowConsentModal(true);
    }
  };

  const handleConsentResponse = async (consent) => {
    setRecordingConsent(consent);
    setShowConsentModal(false);

    // Send consent to server
    await api.saveConsent(meetingId, {
      displayName: user?.name || 'Guest',
      consentType: 'recording',
      consentValue: consent,
      userId: user?.id,
    });

    // Send consent via WebSocket
    webrtcManager.current?.sendMessage({
      type: 'recording-consent',
      consent,
    });

    // Start local recording if consented
    if (consent && localStream) {
      startLocalRecording();
    }
  };

  const startLocalRecording = () => {
    try {
      const options = { mimeType: 'video/webm;codecs=vp8,opus' };
      const recorder = new MediaRecorder(localStream, options);
      const chunks = [];

      recorder.ondataavailable = (event) => {
        if (event.data.size > 0) {
          chunks.push(event.data);
        }
      };

      recorder.onstop = async () => {
        const blob = new Blob(chunks, { type: 'video/webm' });
        await uploadRecording(blob);
      };

      recorder.start();
      setMediaRecorder(recorder);
      setRecording(true);
      setRecordedChunks(chunks);
    } catch (error) {
      console.error('Failed to start recording:', error);
    }
  };

  const stopRecording = () => {
    if (mediaRecorder && mediaRecorder.state !== 'inactive') {
      mediaRecorder.stop();
      webrtcManager.current?.sendMessage({ type: 'stop-recording' });
    }
  };

  const uploadRecording = async (blob) => {
    try {
      const file = new File([blob], `recording-${Date.now()}.webm`, { type: 'video/webm' });
      await api.uploadRecording(meetingId, file, user?.id || participantId.current);
      alert('Recording uploaded successfully!');
    } catch (error) {
      console.error('Failed to upload recording:', error);
      alert('Failed to upload recording');
    }
  };

  const endMeeting = () => {
    if (myRole === 'host') {
      webrtcManager.current?.sendMessage({ type: 'end-meeting' });
    }
    onLeave();
  };

  return (
    <div className="h-screen sketch-surface flex flex-col">
      {/* Header */}
      <div className="sketch-toolbar sketch-toolbar--header px-6 py-4 flex flex-col md:flex-row md:items-center md:justify-between gap-3">
        <div>
          <h2 className="text-xl font-bold sketch-heading flex items-center gap-2">
            <i className="fas fa-signal text-emerald-500"></i>
            Meeting Room
          </h2>
          <p className="text-xs uppercase tracking-[0.3em] text-slate-500">
            {myRole?.toUpperCase()} CHANNEL • {(meetingId || '').slice(0, 8)}
          </p>
        </div>
        <button
          onClick={endMeeting}
          className="sketch-button sketch-button--danger"
        >
          <i className="fas fa-phone-slash"></i>
          {myRole === 'host' ? 'End Meeting' : 'Leave'}
        </button>
      </div>

      {/* Video Grid */}
      <div className="flex-1 overflow-auto p-6 sketch-scroll-area">
        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 auto-rows-fr">
          {/* Local video */}
          <div className="sketch-video-tile aspect-video">
            <video
              ref={localVideoRef}
              autoPlay
              muted
              playsInline
              className="w-full h-full object-cover relative z-0"
            />
            <div className="absolute left-4 bottom-4 sketch-shadow-chip">
              <i className="fas fa-user-astronaut text-emerald-400"></i>
              You {myRole === 'host' && '(Host)'}
            </div>
            {!videoEnabled && (
              <div className="absolute inset-0 flex items-center justify-center sketch-video-off z-[2]">
                <i className="fas fa-video-slash text-4xl text-emerald-200"></i>
              </div>
            )}
          </div>

          {/* Remote videos */}
          {Array.from(participants.values()).map((participant) => (
            <RemoteVideo key={participant.id} participant={participant} />
          ))}
        </div>
      </div>

      {/* Controls */}
      <div className="sketch-toolbar sketch-toolbar--footer px-6 py-4">
        <div className="flex justify-center flex-wrap gap-4">
          <button
            onClick={toggleAudio}
            className={`sketch-control ${audioEnabled ? 'sketch-control--muted sketch-control--active' : 'sketch-control--danger'}`}
            title={audioEnabled ? 'Mute' : 'Unmute'}
          >
            <i className={`fas fa-${audioEnabled ? 'microphone' : 'microphone-slash'}`}></i>
          </button>

          <button
            onClick={toggleVideo}
            className={`sketch-control ${videoEnabled ? 'sketch-control--muted sketch-control--active' : 'sketch-control--danger'}`}
            title={videoEnabled ? 'Stop video' : 'Start video'}
          >
            <i className={`fas fa-${videoEnabled ? 'video' : 'video-slash'}`}></i>
          </button>

          <button
            onClick={toggleScreenShare}
            className={`sketch-control ${screenSharing ? 'sketch-control--primary sketch-control--active' : 'sketch-control--neutral'}`}
            title={screenSharing ? 'Stop sharing' : 'Share screen'}
          >
            <i className="fas fa-desktop"></i>
          </button>

          {(myRole === 'host' || myRole === 'co-host') && !recording && (
            <button
              onClick={startRecording}
              className="sketch-control sketch-control--neutral"
              title="Start recording"
            >
              <i className="fas fa-circle text-red-500"></i>
            </button>
          )}

          {recording && (
            <button
              onClick={stopRecording}
              className="sketch-control sketch-control--danger sketch-control--active"
              title="Stop recording"
            >
              <i className="fas fa-stop"></i>
            </button>
          )}
        </div>
      </div>

      {/* Consent Modal */}
      {showConsentModal && (
        <div className="fixed inset-0 sketch-overlay flex items-center justify-center z-50">
          <div className="sketch-modal max-w-md w-full p-6">
            <h3 className="text-xl font-bold sketch-heading mb-4">Recording Consent</h3>
            <p className="mb-6 text-slate-600">
              The host has started recording this meeting. Do you consent to being recorded?
            </p>
            <div className="flex flex-wrap gap-3">
              <button
                onClick={() => handleConsentResponse(true)}
                className="flex-1 sketch-button sketch-button--success"
              >
                <i className="fas fa-check"></i>
                I Consent
              </button>
              <button
                onClick={() => handleConsentResponse(false)}
                className="flex-1 sketch-button sketch-button--danger"
              >
                <i className="fas fa-times"></i>
                I Do Not Consent
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// Remote Video Component
function RemoteVideo({ participant }) {
  const videoRef = useRef(null);

  useEffect(() => {
    if (videoRef.current && participant.stream) {
      videoRef.current.srcObject = participant.stream;
    }
  }, [participant.stream]);

  return (
    <div className="sketch-video-tile aspect-video">
      <video
        ref={videoRef}
        autoPlay
        playsInline
        className="w-full h-full object-cover relative z-0"
      />
      <div className="absolute left-4 bottom-4 sketch-shadow-chip">
        <i className="fas fa-user text-emerald-300"></i>
        Participant
      </div>
    </div>
  );
}

// ============================================================================
// Invite View Component
// ============================================================================

function InviteView({ inviteData, onJoin }) {
  const { meeting } = inviteData;
  const [guestName, setGuestName] = useState('');
  const [loading, setLoading] = useState(false);

  const handleJoin = () => {
    if (!guestName.trim()) {
      alert('Please enter your name');
      return;
    }

    setLoading(true);
    onJoin(meeting.id);
  };

  const startTime = new Date(meeting.start_time);

  return (
    <div className="min-h-screen sketch-background">
      <div className="sketch-card w-full max-w-lg p-8">
        <div className="text-center mb-6 space-y-3">
          <div className="sketch-shadow-chip mx-auto">
            <i className="fas fa-broadcast-tower text-emerald-500"></i>
            Instant invite
          </div>
          <h1 className="text-3xl font-bold sketch-heading">You're Invited!</h1>
          <p className="text-slate-500">Join {meeting.title}</p>
        </div>

        <div className="sketch-card p-4 mb-6">
          <h2 className="text-xl font-bold sketch-heading mb-2">{meeting.title}</h2>
          {meeting.description && (
            <p className="text-slate-500 mb-3">{meeting.description}</p>
          )}
          <div className="flex flex-wrap items-center gap-4 text-sm text-slate-500">
            <span className="flex items-center gap-2">
              <i className="fas fa-calendar"></i>
              {startTime.toLocaleDateString()}
            </span>
            <span className="flex items-center gap-2">
              <i className="fas fa-clock"></i>
              {startTime.toLocaleTimeString()}
            </span>
          </div>
        </div>

        <div className="space-y-5">
          <div>
            <label className="sketch-label">Your name</label>
            <input
              type="text"
              value={guestName}
              onChange={(e) => setGuestName(e.target.value)}
              placeholder="Enter your name"
              className="sketch-input"
              disabled={loading}
            />
          </div>

          <button
            onClick={handleJoin}
            disabled={loading}
            className="w-full sketch-button sketch-button--primary disabled:opacity-70 disabled:cursor-not-allowed"
          >
            {loading ? (
              <>
                <i className="fas fa-spinner fa-spin"></i>
                Joining...
              </>
            ) : (
              <>
                <i className="fas fa-video"></i>
                Join Meeting
              </>
            )}
          </button>
        </div>

        <p className="text-center text-sm text-slate-500 mt-6">
          By joining, you agree to be on camera and may be recorded.
        </p>
      </div>
    </div>
  );
}

// ============================================================================
// Main App Component
// ============================================================================

function App({ onStateChange }) {
  const [user, setUser] = useState(null);
  const [token, setToken] = useState(null);
  const [currentView, setCurrentView] = useState('auth');
  const [activeMeetingId, setActiveMeetingId] = useState(null);
  const [inviteToken, setInviteToken] = useState(null);
  const [inviteData, setInviteData] = useState(null);

  // Update global app state for error logging
  useEffect(() => {
    if (onStateChange) {
      onStateChange({ user, token });
    }
  }, [user, token, onStateChange]);

  useEffect(() => {
    // Check if this is an invite link
    const pathname = window.location.pathname;
    const inviteMatch = pathname.match(/^\/join\/(.+)$/);
    
    if (inviteMatch) {
      const token = inviteMatch[1];
      setInviteToken(token);
      
      // Fetch invite details
      api.getInvite(token)
        .then(data => {
          if (data.error) {
            alert('Invalid or expired invite link');
            window.history.pushState({}, '', '/');
          } else {
            setInviteData(data);
            setCurrentView('invite');
          }
        })
        .catch(err => {
          alert('Failed to load invite');
          window.history.pushState({}, '', '/');
        });
      return;
    }

    // Check for stored credentials
    const storedToken = localStorage.getItem('token');
    const storedUser = localStorage.getItem('user');

    if (storedToken && storedUser) {
      setToken(storedToken);
      setUser(JSON.parse(storedUser));
      setCurrentView('dashboard');
    }
  }, []);

  const handleLogin = (userData, userToken) => {
    setUser(userData);
    setToken(userToken);
    setCurrentView('dashboard');
  };

  const handleLogout = () => {
    localStorage.removeItem('token');
    localStorage.removeItem('user');
    setUser(null);
    setToken(null);
    setCurrentView('auth');
  };

  const handleJoinMeeting = (meetingId) => {
    setActiveMeetingId(meetingId);
    setCurrentView('room');
  };

  const handleLeaveMeeting = () => {
    setActiveMeetingId(null);
    setCurrentView('dashboard');
  };

  if (currentView === 'invite' && inviteData) {
    return <InviteView 
      inviteData={inviteData} 
      onJoin={(meetingId) => {
        setActiveMeetingId(meetingId);
        setCurrentView('room');
        window.history.pushState({}, '', '/');
      }}
    />;
  }

  if (currentView === 'auth') {
    return <Auth onLogin={handleLogin} />;
  }

  if (currentView === 'dashboard') {
    // Use enhanced dashboard if available, fallback to original
    const DashboardComponent = window.EnhancedDashboard || Dashboard;
    return (
      <DashboardComponent
        user={user}
        token={token}
        onLogout={handleLogout}
        onJoinMeeting={handleJoinMeeting}
      />
    );
  }

  if (currentView === 'room' && activeMeetingId) {
    return (
      <VideoRoom
        meetingId={activeMeetingId}
        user={user}
        onLeave={handleLeaveMeeting}
      />
    );
  }

  return null;
}

// Wrapped App Component with Enhancements
function WrappedApp() {
  const NotificationProvider = window.EnhancedComponents?.NotificationProvider;
  const GlobalErrorBoundary = window.ErrorBoundary; // New global error boundary

  // Store app instance globally for error logging
  const [appState, setAppState] = useState(null);
  
  useEffect(() => {
    window.cloudRTCApp = appState;
  }, [appState]);

  if (!NotificationProvider) {
    // Fallback to regular app if enhancements not loaded
    return (
      <GlobalErrorBoundary>
        <App onStateChange={setAppState} />
      </GlobalErrorBoundary>
    );
  }

  return (
    <GlobalErrorBoundary>
      <NotificationProvider>
        <App onStateChange={setAppState} />
      </NotificationProvider>
    </GlobalErrorBoundary>
  );
}

// Render the app
ReactDOM.render(<WrappedApp />, document.getElementById('root'));
