/* VDO.Ninja Authentication Client Integration */

// Configuration
const AUTH_SERVICE_URL = 'https://vdo-ninja-auth-service.vdo.workers.dev'; // Change for local dev: http://localhost:8787

// Authentication state
session.authMode = false;
session.requireAuth = false;
session.authToken = null;
session.authUser = null;
session.authStreamMapping = {};
session.handleToStream = {};


// Initialize authentication
async function initAuthentication() {
  
  // Check URL parameters for universal token first
  if (urlParams.has("universaltoken")) {
    session.universalToken = urlParams.get("universaltoken");
    session.authMode = true;
    console.log('Universal token detected:', session.universalToken);
    // Universal tokens bypass auth requirement for viewing
    if (session.view || session.scene || session.solo) {
      session.requireAuth = false;
      console.log('Auth requirement bypassed for viewing');
    }
  }
  
  // Check URL parameters
  if (urlParams.has("auth") || urlParams.has("requireauth")) {
    session.authMode = true;
    session.requireAuth = urlParams.has("requireauth");
    
    // Check for existing auth token in localStorage
    const storedToken = localStorage.getItem('vdo_auth_token');
    if (storedToken) {
      try {
        // Validate token is still valid
        const payload = JSON.parse(atob(storedToken.split('.')[1]));
        if (payload.exp > Date.now() / 1000) {
          session.authToken = storedToken;
          await populateUserInfo();
        } else {
          localStorage.removeItem('vdo_auth_token');
        }
      } catch (e) {
        localStorage.removeItem('vdo_auth_token');
      }
    }
    
    // Check for auth token in URL (after OAuth redirect)
    if (urlParams.has("authtoken")) {
      session.authToken = urlParams.get("authtoken");
      localStorage.setItem('vdo_auth_token', session.authToken);
      
      // Clean URL
      const url = new URL(window.location.href);
      url.searchParams.delete('authtoken');
      window.history.replaceState({}, document.title, url.toString());
      
      await populateUserInfo();
    }
    
    // Check if we need to verify room requirements
    if (!session.authToken && session.authMode && (urlParams.has("room") || urlParams.has("roomid") || urlParams.has("r"))) {
      const roomId = urlParams.get("room") || urlParams.get("roomid") || urlParams.get("r");
      if (roomId) {
        // Check if this room requires auth
        try {
          const roomInfo = await checkRoomAccess(roomId, urlParams.has("director") || urlParams.has("dir"));
          if (roomInfo.requiresAuth) {
            session.requireAuth = true;
          }
        } catch (e) {
          console.log('Could not check room requirements:', e);
        }
      }
    }
    
    // Show auth UI if required and not authenticated
    if (!session.authToken && (session.requireAuth || session.director)) {
      // If the page is in auth mode or the director is attempting to use auth,
      // encourage sign-in proactively.
      showAuthUI();
    }
  }
}

// Show authentication UI
function showAuthUI(options = {}) {
  const authContainer = document.createElement('div');
  authContainer.id = 'auth-container';
  authContainer.innerHTML = `
    <div class="auth-modal">
      <h2>Sign in to VDO.Ninja</h2>
      <p>${options.message || 'Sign in to claim your personal stream ID and enable advanced features'}</p>
      
      <div class="auth-buttons">
        <button onclick="socialSignIn('google')" class="auth-button google">
          <img src="./media/google.png" alt="Google">
          Sign in with Google
        </button>
        <button onclick="socialSignIn('discord')" class="auth-button discord">
          <img src="./media/discord.png" alt="Discord">
          Sign in with Discord
        </button>
        <button onclick="socialSignIn('twitch')" class="auth-button twitch">
          <img src="./media/twitch.png" alt="Twitch">
          Sign in with Twitch
        </button>
      </div>
      
      ${(!session.requireAuth && !options.requireAuth) ? '<button onclick="skipAuth()" class="skip-auth">Continue without signing in</button>' : ''}
    </div>
  `;
  
  document.body.appendChild(authContainer);
}

// Social sign-in handler
function socialSignIn(provider) {
  const returnUrl = encodeURIComponent(window.location.href);
  window.location.href = `${AUTH_SERVICE_URL}/auth/${provider}?returnUrl=${returnUrl}`;
}

// Skip authentication
function skipAuth() {
  const authContainer = document.getElementById('auth-container');
  if (authContainer) {
    authContainer.remove();
  }
  session.authSkipped = true;
}

// Populate user info from auth token
async function populateUserInfo() {
  if (!session.authToken) return;
  
  try {
    const response = await fetch(`${AUTH_SERVICE_URL}/api/user/info`, {
      headers: { 'Authorization': `Bearer ${session.authToken}` }
    });
    
    if (response.ok) {
      const userInfo = await response.json();
      session.authUser = userInfo;
      
      // Auto-populate label if not set
      if (!session.label && userInfo.displayName) {
        session.label = userInfo.displayName;
        if (document.getElementById("label_input")) {
          document.getElementById("label_input").value = session.label;
        }
      }
      
      // Auto-populate avatar if not set
      if (!session.avatar && userInfo.avatar) {
        session.avatar = userInfo.avatar;
        updateAvatarDisplay();
      }
      
      // Store user handle
      session.userHandle = userInfo.userHandle;
      
      // Show user info in UI
      showUserInfo(userInfo);
    }
  } catch (e) {
    console.error("Failed to get user info:", e);
  }
}

// Show user info in UI
function showUserInfo(userInfo) {
  const existingDisplay = document.getElementById('user-info-display');
  if (existingDisplay) {
    existingDisplay.remove();
  }
  
  const userDisplay = document.createElement('div');
  userDisplay.id = 'user-info-display';
  userDisplay.className = 'user-info-display';
  userDisplay.innerHTML = `
    <img src="${userInfo.avatar || './media/default-avatar.png'}" alt="${userInfo.displayName}">
    <div class="user-details">
      <div class="user-name">${userInfo.displayName}</div>
      <div class="user-handle">${userInfo.userHandle}</div>
    </div>
  `;
  
  // Add to appropriate location based on current view
  const targetElement = document.querySelector('.header-container') || document.querySelector('.container');
  if (targetElement) {
    targetElement.insertBefore(userDisplay, targetElement.firstChild);
  }
}

// Assign authenticated stream ID
async function assignAuthStream() {
  if (!session.authToken || session.authStreamAssigned) return;
  
  try {
    const response = await fetch(`${AUTH_SERVICE_URL}/api/stream/assign`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${session.authToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        roomId: session.roomid || 'lobby',
        deviceLabel: session.streamID || 'camera',
        useEncryption: false // Disabled for now until fully tested
      })
    });
    
    if (response.ok) {
      const assignment = await response.json();
      
      // Store original stream ID
      session.originalStreamID = session.streamID;
      
      // Use assigned stream ID
      session.streamID = assignment.streamId;
      session.streamSecret = assignment.streamSecret;
      session.authStreamAssigned = true;
      
      console.log("Assigned authenticated stream:", assignment.streamId);
      
      // Update any UI showing stream ID
      updateStreamIDDisplay();
    }
  } catch (e) {
    console.error("Failed to assign auth stream:", e);
  }
}

// Generate stream authentication signature
async function generateStreamSignature() {
  if (!session.streamSecret) return null;
  
  const timestamp = Date.now();
  const message = `${session.streamID}:${timestamp}`;
  
  const encoder = new TextEncoder();
  const key = await crypto.subtle.importKey(
    'raw',
    encoder.encode(session.streamSecret),
    { name: 'HMAC', hash: 'SHA-256' },
    false,
    ['sign']
  );
  
  const signature = await crypto.subtle.sign('HMAC', key, encoder.encode(message));
  const hexSignature = Array.from(new Uint8Array(signature))
    .map(b => b.toString(16).padStart(2, '0'))
    .join('');
  
  return {
    streamId: session.streamID,
    userHandle: session.userHandle,
    timestamp: timestamp,
    signature: hexSignature
  };
}

// Validate incoming stream authentication
async function validateStreamAuth(streamId, authData) {
  if (!session.authToken || !authData) return true;
  
  try {
    const response = await fetch(`${AUTH_SERVICE_URL}/api/stream/verify`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${session.authToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        streamId: streamId,
        auth: authData
      })
    });
    
    if (response.ok) {
      const result = await response.json();
      if (result.valid && result.userInfo) {
        // Store user info for this stream
        session.authStreamMapping[streamId] = result.userInfo;
        
        // Update UI if this is a director view
        if (session.director) {
          updateStreamDisplay(streamId, result.userInfo);
        }
      }
      return result.valid;
    }
  } catch (e) {
    console.error("Stream validation failed:", e);
  }
  
  return false;
}

// Resolve view handles (e.g., @johndoe) to stream IDs
async function resolveViewHandles(viewList) {
  if (!session.authToken) return viewList;
  
  const resolved = [];
  
  for (const target of viewList) {
    if (target.startsWith('@')) {
      // User handle - resolve to current stream
      try {
        const response = await fetch(`${AUTH_SERVICE_URL}/api/stream/user/${target}`, {
          headers: { 'Authorization': `Bearer ${session.authToken}` }
        });
        
        if (response.ok) {
          const data = await response.json();
          if (data.currentStreamId) {
            resolved.push(data.currentStreamId);
            // Store mapping for UI
            session.handleToStream[target] = data;
          }
        }
      } catch (e) {
        console.error(`Failed to resolve handle ${target}:`, e);
      }
    } else {
      resolved.push(target);
    }
  }
  
  return resolved;
}

// Check room access
async function checkRoomAccess(roomIdOrAlias, isDirector = false) {
  console.log('Checking room access for:', roomIdOrAlias, 'with universal token:', session.universalToken);
  const response = await fetch(`${AUTH_SERVICE_URL}/api/room/access`, {
    method: 'POST',
    headers: {
      'Authorization': session.authToken ? `Bearer ${session.authToken}` : '',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ 
      room: roomIdOrAlias,
      isDirector: isDirector,
      universalToken: session.universalToken || null
    })
  });
  
  const data = await response.json();
  console.log('Room access response:', response.status, data);
  
  // Handle room not found case
  if (response.status === 404 && data && data.error === 'Room not found') {
    // In auth mode, non-existent rooms can be created by authenticated users
    if (session.authToken) {
      // Allow authenticated users to proceed - room will be created on first join
      return {
        roomId: roomIdOrAlias,
        alias: roomIdOrAlias,
        displayName: roomIdOrAlias,
        requiresAuth: false,
        hasAccess: true,
        isNew: true
      };
    } else {
      // Require auth to create new rooms
      return {
        alias: roomIdOrAlias,
        displayName: roomIdOrAlias,
        requiresAuth: true,
        hasAccess: false,
        accessDenied: true,
        denialReason: 'Sign in to create or join this room'
      };
    }
  }
  
  return data;
}

// Join room with authentication
async function joinRoomWithAuth(roomIdOrAlias) {
  // If director is using auth mode but not signed in yet, force sign in first
  if (session.director && session.authMode && !session.authToken && !session.universalToken) {
    const roomLabel = roomIdOrAlias || 'this room';
    showAuthUI({
      message: `Sign in to manage "${roomLabel}"`,
      requireAuth: true
    });
    return false;
  }
  // If we have a universal token, validate it first
  if (session.universalToken) {
    try {
      const response = await fetch(`${AUTH_SERVICE_URL}/api/room/validate-universal`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          token: session.universalToken,
          roomId: roomIdOrAlias
        })
      });
      
      if (response.ok) {
        const result = await response.json();
        if (result.valid) {
          // Universal token is valid, bypass normal auth
          session.roomid = roomIdOrAlias;
          return true;
        }
      }
    } catch (e) {
      console.error('Failed to validate universal token:', e);
    }
  }
  
  const roomInfo = await checkRoomAccess(roomIdOrAlias, session.director);
  
  if (roomInfo.requiresAuth && !session.authToken && !session.universalToken) {
    if (session.authSkipped) {
      // User already chose to skip auth, show access denied instead of auth UI
      showAccessDeniedUI({
        ...roomInfo,
        denialReason: 'This room requires authentication. Please reload the page and sign in to join.',
        requestAccessUrl: null
      });
      return false;
    } else {
      // First time seeing auth requirement for this room
      const displayLabel = roomInfo.displayName || roomInfo.alias || roomIdOrAlias || roomInfo.roomId || 'this room';
      showAuthUI({
        message: `Sign in to join "${displayLabel}"`,
        requireAuth: true
      });
      return false;
    }
  }
  
  if (roomInfo.accessDenied) {
    showAccessDeniedUI(roomInfo);
    return false;
  }
  
  // Important: For auth rooms, we need to use the original alias for hashing
  // The auth service tracks by the real room ID, but VDO uses the alias
  if (roomInfo.alias && roomInfo.alias === roomIdOrAlias) {
    // User provided the alias, keep using it
    session.roomid = roomIdOrAlias;
  } else if (roomInfo.roomId === roomIdOrAlias) {
    // User provided the real room ID
    session.roomid = roomInfo.alias || roomIdOrAlias;
  } else {
    // Default case
    session.roomid = roomInfo.alias || roomInfo.roomId;
  }
  
  session.roomAlias = roomInfo.alias;
  session.realRoomId = roomInfo.roomId;
  
  return true;
}

// Show access denied UI
function showAccessDeniedUI(roomInfo) {
  const modal = document.createElement('div');
  modal.id = 'auth-container';
  modal.innerHTML = `
    <div class="auth-modal access-denied-modal">
      <h3>Access Denied</h3>
      <p>${roomInfo.denialReason}</p>
      ${roomInfo.requestAccessUrl ? 
        `<button onclick="requestRoomAccess('${roomInfo.roomId}')">Request Access</button>` : 
        '<button onclick="window.location.reload()">Go Back</button>'
      }
    </div>
  `;
  
  document.body.appendChild(modal);
}

// Request room access
async function requestRoomAccess(roomId) {
  if (!session.authToken) {
    showAuthUI({ message: 'Sign in to request access' });
    return;
  }
  
  try {
    const response = await fetch(`${AUTH_SERVICE_URL}/api/room/request-access/${roomId}`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${session.authToken}`
      }
    });
    
    if (response.ok) {
      alert('Access request sent! The room owner will review your request.');
      document.getElementById('auth-container').remove();
    }
  } catch (e) {
    console.error('Failed to request access:', e);
  }
}

// Update stream display with user info
function updateStreamDisplay(streamId, userInfo) {
  // Update control box if it exists
  const controlBox = document.getElementById(`controls_${streamId}`);
  if (controlBox && userInfo) {
    const header = controlBox.querySelector('.header');
    if (header && !header.querySelector('.user-auth-badge')) {
      const badge = document.createElement('div');
      badge.className = 'user-auth-badge';
      badge.innerHTML = `
        <img src="${userInfo.avatar}" alt="${userInfo.displayName}">
        <span class="user-handle">${userInfo.userHandle}</span>
        <span class="user-provider ${userInfo.provider}">${userInfo.provider}</span>
      `;
      header.appendChild(badge);
    }
  }
  
  // Update any labels showing stream ID
  const labels = document.querySelectorAll(`[data-stream-id="${streamId}"]`);
  labels.forEach(label => {
    if (userInfo && !label.dataset.updated) {
      label.dataset.updated = 'true';
      label.textContent = userInfo.displayName || userInfo.userHandle;
    }
  });
}

// Update avatar display
function updateAvatarDisplay() {
  if (session.avatar) {
    // Update any avatar displays in the UI
    const avatarElements = document.querySelectorAll('.avatar-display');
    avatarElements.forEach(el => {
      el.src = session.avatar;
    });
  }
}

// Update stream ID display
function updateStreamIDDisplay() {
  // Update any UI elements showing the stream ID
  const streamIdElements = document.querySelectorAll('.stream-id-display');
  streamIdElements.forEach(el => {
    el.textContent = session.originalStreamID || session.streamID;
  });
}

// Resolve any stream ID (encrypted or not) through auth service
async function resolveStream(streamId) {
  if (!session.authToken && !session.universalToken) {
    return { error: 'Not authenticated' };
  }
  
  try {
    const headers = {
      'Content-Type': 'application/json'
    };
    
    if (session.authToken) {
      headers['Authorization'] = `Bearer ${session.authToken}`;
    }
    
    const response = await fetch(`${AUTH_SERVICE_URL}/api/stream/resolve`, {
      method: 'POST',
      headers: headers,
      body: JSON.stringify({
        streamId: streamId,
        roomId: session.roomid,
        universalToken: session.universalToken
      })
    });
    
    if (response.ok) {
      const data = await response.json();
      return data;
    } else if (response.status === 403) {
      return { error: 'Access denied' };
    } else if (response.status === 404) {
      return { error: 'Stream not found' };
    }
  } catch (e) {
    console.error('Failed to resolve stream:', e);
    return { error: 'Failed to resolve stream' };
  }
  
  return { error: 'Unknown error' };
}

// Get encryption key for viewing a stream
async function getStreamKey(streamId) {
  if (!session.authToken) return null;
  
  try {
    const response = await fetch(`${AUTH_SERVICE_URL}/api/stream/key`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${session.authToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        streamId: streamId,
        roomId: session.roomid
      })
    });
    
    if (response.ok) {
      const data = await response.json();
      return data;
    }
  } catch (e) {
    console.error('Failed to get stream key:', e);
  }
  
  return null;
}

// Decrypt stream ID using XOR cipher
async function decryptStreamId(encryptedId, key) {
  // Add padding if needed
  const base64 = encryptedId
    .replace(/-/g, '+')
    .replace(/_/g, '/')
    .padEnd(encryptedId.length + (4 - encryptedId.length % 4) % 4, '=');
  
  const encrypted = Uint8Array.from(atob(base64), c => c.charCodeAt(0));
  const keyData = new TextEncoder().encode(key);
  
  const decrypted = new Uint8Array(encrypted.length);
  for (let i = 0; i < encrypted.length; i++) {
    decrypted[i] = encrypted[i] ^ keyData[i % keyData.length];
  }
  
  return new TextDecoder().decode(decrypted);
}

// Heartbeat to keep stream active
function startAuthHeartbeat() {
  if (!session.authToken || !session.streamID) return;
  
  setInterval(async () => {
    if (session.authToken && session.streamID && session.authStreamAssigned) {
      try {
        await fetch(`${AUTH_SERVICE_URL}/api/stream/heartbeat`, {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${session.authToken}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            streamId: session.streamID,
            roomId: session.roomid || 'lobby'
          })
        });
      } catch (e) {
        console.error('Heartbeat failed:', e);
      }
    }
  }, 30000); // Every 30 seconds
}

// Create a universal token for view/scene links
async function createUniversalToken() {
  if (!session.authToken || !session.roomid) {
    console.error('Must be authenticated and in a room to create universal token');
    return null;
  }
  
  try {
    console.log('Creating universal token for room:', session.roomid);
    const response = await fetch(`${AUTH_SERVICE_URL}/api/room/universal-token`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${session.authToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        roomId: session.roomid,
        description: 'View/Scene access token'
      })
    });
    
    if (response.ok) {
      const data = await response.json();
      session.universalViewToken = data.token;
      console.log('Created universal token:', data.token);
      
      // Update all existing solo links
      updateAllSoloLinks();
      
      return data.token;
    } else {
      console.error('Failed to create universal token:', response.status);
    }
  } catch (e) {
    console.error('Failed to create universal token:', e);
  }
  
  return null;
}

// Update all solo link displays with new token
function updateAllSoloLinks() {
  // Update all solo link inputs and displays
  document.querySelectorAll('[data-sololink]').forEach(ele => {
    const uuid = ele.getAttribute('data--u-u-i-d');
    if (uuid && session.rpcs[uuid]) {
      const soloLink = soloLinkGenerator(session.rpcs[uuid].streamID, false);
      if (ele.tagName === 'INPUT') {
        ele.value = soloLink;
      } else if (ele.tagName === 'A') {
        ele.href = soloLink;
        ele.innerText = soloLink;
      }
    }
  });
  
  // Update director's own solo link if present
  const directorLink = document.querySelector('#grabDirectorSoloLink');
  if (directorLink && session.streamID) {
    const soloLink = soloLinkGenerator(session.streamID, true);
    directorLink.dataset.raw = soloLink;
    directorLink.href = soloLink;
    directorLink.innerText = soloLink;
  }
  
  // Update solo links in control boxes
  document.querySelectorAll('.soloLink').forEach(ele => {
    if (ele.getAttribute('value')) {
      const baseUrl = ele.getAttribute('value');
      // Extract stream ID from the base URL
      const match = baseUrl.match(/[?&]view=([^&]+)/);
      if (match && match[1]) {
        const streamId = match[1];
        const soloLink = soloLinkGenerator(streamId, false);
        ele.href = soloLink;
        ele.innerHTML = soloLink;
      }
    }
  });
}

// Update room settings (access mode, allowlist)
async function updateRoomSettings(roomId, settings) {
  if (!session.authToken) {
    console.error('Must be authenticated to update room settings');
    return null;
  }
  
  try {
    const response = await fetch(`${AUTH_SERVICE_URL}/api/room/settings/${roomId}`, {
      method: 'PUT',
      headers: {
        'Authorization': `Bearer ${session.authToken}`,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify(settings)
    });
    
    if (response.ok) {
      const data = await response.json();
      console.log('Room settings updated');
      return data;
    } else {
      console.error('Failed to update room settings:', response.status);
    }
  } catch (e) {
    console.error('Failed to update room settings:', e);
  }
  
  return null;
}

// Get pending access requests for a room
async function getRoomAccessRequests(roomId) {
  if (!session.authToken) {
    console.error('Must be authenticated to get access requests');
    return [];
  }
  
  try {
    const response = await fetch(`${AUTH_SERVICE_URL}/api/room/requests/${roomId}`, {
      headers: {
        'Authorization': `Bearer ${session.authToken}`
      }
    });
    
    if (response.ok) {
      return await response.json();
    }
  } catch (e) {
    console.error('Failed to get access requests:', e);
  }
  
  return [];
}

// Approve or deny an access request
async function handleAccessRequest(roomId, userId, action) {
  if (!session.authToken) {
    console.error('Must be authenticated to handle access requests');
    return false;
  }
  
  try {
    const response = await fetch(`${AUTH_SERVICE_URL}/api/room/request/${roomId}/${userId}/${action}`, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${session.authToken}`
      }
    });
    
    return response.ok;
  } catch (e) {
    console.error('Failed to handle access request:', e);
  }
  
  return false;
}

// Export functions for use in main VDO.Ninja code
window.vdoAuth = {
  init: initAuthentication,
  assignStream: assignAuthStream,
  generateSignature: generateStreamSignature,
  validateStream: validateStreamAuth,
  resolveHandles: resolveViewHandles,
  checkRoomAccess: checkRoomAccess,
  joinRoom: joinRoomWithAuth,
  startHeartbeat: startAuthHeartbeat,
  getStreamKey: getStreamKey,
  decryptStreamId: decryptStreamId,
  resolveStream: resolveStream,
  createUniversalToken: createUniversalToken,
  updateRoomSettings: updateRoomSettings,
  getRoomAccessRequests: getRoomAccessRequests,
  handleAccessRequest: handleAccessRequest
};
