import { useCallback } from 'react';
import { Room } from 'matrix-js-sdk';
import { useMatrixClient } from '../../hooks/useMatrixClient';
import { LiveKitFocus, LiveKitJWTResponse, OpenIdToken, WellKnownWithRTC } from './types';
import { getMxIdServer } from '../../utils/matrix';
import { guessDmRoomUserId } from '../../utils/matrix';
import { getActiveCallMembers } from './useRoomCallMembers';

/**
 * Get room server from room ID (format: !roomid:server.com)
 */
function getRoomServer(roomId: string): string | undefined {
  const server = getMxIdServer(roomId);
  console.log(`[getRoomServer] Room ID: ${roomId} -> Server: ${server}`);
  return server;
}

/**
 * Determine which homeservers to try for LiveKit JWT, in priority order
 * Prioritizes servers where active call members are located when joining existing calls
 * @param room - Room to start/join call in
 * @param userHomeserver - User's homeserver
 * @param myUserId - Current user's Matrix ID
 * @returns Array of homeserver base URLs to try in order
 */
export function getLiveKitHomeserverPriority(
  room: Room,
  userHomeserver: string,
  myUserId: string
): string[] {
  const servers: string[] = [];
  const memberCount = room.getMembers().length;
  const isDM = memberCount <= 2;
  
  console.log(`[getLiveKitHomeserverPriority] Room: ${room.roomId}`);
  console.log(`[getLiveKitHomeserverPriority] Member count: ${memberCount}, isDM: ${isDM}`);
  console.log(`[getLiveKitHomeserverPriority] User homeserver: ${userHomeserver}`);
  
  // Check for active call members
  const activeCallMembers = getActiveCallMembers(room);
  const hasActiveCall = activeCallMembers.length > 0;

  console.log(`[getLiveKitHomeserverPriority] Active call members: ${activeCallMembers.length}, hasActiveCall: ${hasActiveCall}`);

  if (hasActiveCall) {
    // Joining existing call - prioritize room's homeserver (where call is hosted)
    const roomServer = getRoomServer(room.roomId);
    console.log(`[getLiveKitHomeserverPriority] Room server (active call): ${roomServer}`);
    if (roomServer) {
      servers.push(`https://${roomServer}`);
    }
    
    // Add user's homeserver as fallback
    const userDomain = userHomeserver.replace('https://', '').replace('http://', '');
    if (!roomServer || roomServer !== userDomain) {
      servers.push(userHomeserver);
    }
    
    // For DMs, also try other member's homeserver
    if (isDM) {
      const otherUserId = guessDmRoomUserId(room, myUserId);
      if (otherUserId && otherUserId !== myUserId) {
        const otherServer = getMxIdServer(otherUserId);
        if (otherServer && otherServer !== userDomain && otherServer !== roomServer) {
          servers.push(`https://${otherServer}`);
        }
      }
    }
  } else {
    // Starting new call - use original priority logic
    if (isDM) {
      console.log(`[getLiveKitHomeserverPriority] DM path - user homeserver first`);
      // For DMs: user's homeserver first, then other member's homeserver
      servers.push(userHomeserver);
      
      const otherUserId = guessDmRoomUserId(room, myUserId);
      console.log(`[getLiveKitHomeserverPriority] Other user ID: ${otherUserId}`);
      if (otherUserId && otherUserId !== myUserId) {
        const otherServer = getMxIdServer(otherUserId);
        console.log(`[getLiveKitHomeserverPriority] Other user server: ${otherServer}`);
        const userDomain = userHomeserver.replace('https://', '').replace('http://', '');
        if (otherServer && otherServer !== userDomain) {
          servers.push(`https://${otherServer}`);
        }
      }
    } else {
      console.log(`[getLiveKitHomeserverPriority] Channel path - room homeserver first`);
      // For channels: room's homeserver first, then user's homeserver
      const roomServer = getRoomServer(room.roomId);
      console.log(`[getLiveKitHomeserverPriority] Room server from ID: ${roomServer}`);
      if (roomServer) {
        servers.push(`https://${roomServer}`);
      }
      
      // Fallback to user's homeserver if different
      const userDomain = userHomeserver.replace('https://', '').replace('http://', '');
      console.log(`[getLiveKitHomeserverPriority] User domain: ${userDomain}, room server: ${roomServer}`);
      if (!roomServer || roomServer !== userDomain) {
        servers.push(userHomeserver);
      }
    }
  }

  console.log(`[getLiveKitHomeserverPriority] Final server list:`, servers);
  return servers;
}

/**
 * Fetches the LiveKit focus configuration from well-known data
 * @param wellKnown - The well-known response that may contain RTC foci
 * @returns The LiveKit focus configuration if available
 */
export function getLiveKitFocus(wellKnown: WellKnownWithRTC): LiveKitFocus | undefined {
  const rtcFoci = wellKnown['org.matrix.msc4143.rtc_foci'];
  console.log(`[getLiveKitFocus] rtcFoci from well-known:`, rtcFoci);
  
  if (!rtcFoci || !Array.isArray(rtcFoci)) {
    console.log(`[getLiveKitFocus] No rtc_foci found or not an array`);
    return undefined;
  }

  const livekitFocus = rtcFoci.find((focus): focus is LiveKitFocus => focus.type === 'livekit');
  console.log(`[getLiveKitFocus] Found LiveKit focus:`, livekitFocus);
  return livekitFocus;
}

/**
 * Fetches the well-known client configuration from a homeserver
 * @param baseUrl - The homeserver base URL
 * @returns The well-known response with RTC configuration
 */
export async function fetchWellKnownWithRTC(baseUrl: string): Promise<WellKnownWithRTC> {
  // In development mode (localhost), use CORS proxy to avoid CORS issues
  const isDev = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
  
  let wellKnownUrl: string;
  if (isDev) {
    // Extract hostname from baseUrl for proxy
    const hostname = baseUrl.replace('https://', '').replace('http://', '').replace(/\/$/, '');
    wellKnownUrl = `/_matrix-cors-proxy/${hostname}/.well-known/matrix/client`;
  } else {
    wellKnownUrl = `${baseUrl}/.well-known/matrix/client`;
  }

  console.log(`[fetchWellKnownWithRTC] Fetching from: ${wellKnownUrl}`);

  const response = await fetch(wellKnownUrl);
  if (!response.ok) {
    throw new Error(`Failed to fetch well-known: ${response.status}`);
  }

  const data = await response.json();
  console.log(`[fetchWellKnownWithRTC] Response data:`, data);
  return data;
}

/**
 * Requests an OpenID token from the Matrix homeserver
 * This token can be used to authenticate with third-party services
 * @param homeserverBaseUrl - The Matrix homeserver base URL
 * @param userId - The user's Matrix ID
 * @param accessToken - The Matrix access token
 * @returns The OpenID token response
 */
export async function requestOpenIdToken(
  homeserverBaseUrl: string,
  userId: string,
  accessToken: string
): Promise<OpenIdToken> {
  // In development mode (localhost), use CORS proxy to avoid CORS issues
  const isDev = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
  
  let url: string;
  if (isDev) {
    const hostname = homeserverBaseUrl.replace('https://', '').replace('http://', '').replace(/\/$/, '');
    url = `/_matrix-cors-proxy/${hostname}/_matrix/client/v3/user/${encodeURIComponent(userId)}/openid/request_token`;
  } else {
    url = `${homeserverBaseUrl}/_matrix/client/v3/user/${encodeURIComponent(userId)}/openid/request_token`;
  }

  const response = await fetch(url, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${accessToken}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({}),
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`Failed to request OpenID token: ${response.status} - ${errorText}`);
  }

  return response.json();
}

/**
 * Fetches a LiveKit JWT for joining a call using OpenID token authentication
 * @param livekitServiceUrl - The LiveKit service URL from Matrix homeserver well-known
 * @param roomId - The Matrix room ID to join
 * @param userId - The Matrix user ID
 * @param deviceId - The Matrix device ID
 * @param openIdToken - The OpenID token from the Matrix homeserver
 * @returns The LiveKit JWT and server URL
 */
export async function fetchLiveKitJWT(
  livekitServiceUrl: string,
  roomId: string,
  userId: string,
  deviceId: string,
  openIdToken: OpenIdToken
): Promise<LiveKitJWTResponse> {
  // Generate a unique member ID for this participant (used for identity, not room assignment)
  const memberId = `${deviceId}_${Date.now()}`;

  // slot_id must be constant so all participants in the same Matrix room
  // get assigned to the same LiveKit room. The JWT service hashes room_id + slot_id
  // to create the LiveKit room name.
  const slotId = 'm.call';

  // In development mode (localhost), use CORS proxy to avoid CORS issues
  const isDev = window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1';
  
  let url: string;
  if (isDev) {
    // Extract hostname and path from livekitServiceUrl
    const urlObj = new URL(livekitServiceUrl);
    const hostname = urlObj.hostname;
    const pathname = urlObj.pathname;
    url = `/_matrix-cors-proxy/${hostname}${pathname}`;
  } else {
    url = livekitServiceUrl;
  }

  const response = await fetch(url, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      openid_token: openIdToken,
      room_id: roomId,
      slot_id: slotId,
      member: {
        id: memberId,
        claimed_user_id: userId,
        claimed_device_id: deviceId,
      },
    }),
  });

  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`Failed to fetch LiveKit JWT: ${response.status} - ${errorText}`);
  }

  return response.json();
}

/**
 * Try fetching LiveKit JWT from multiple homeservers in priority order
 * @param homeservers - Array of homeserver base URLs to check for LiveKit config
 * @param userHomeserver - The user's own homeserver URL (for OpenID token)
 * @param roomId - The Matrix room ID
 * @param userId - The Matrix user ID
 * @param deviceId - The Matrix device ID
 * @param accessToken - The Matrix access token
 * @returns LiveKit JWT response and the homeserver that provided it, or null
 */
export async function fetchLiveKitJWTFromServers(
  homeservers: string[],
  userHomeserver: string,
  roomId: string,
  userId: string,
  deviceId: string,
  accessToken: string
): Promise<{ jwt: LiveKitJWTResponse; homeserver: string } | null> {
  for (const homeserver of homeservers) {
    try {
      console.log(`Trying LiveKit JWT from homeserver: ${homeserver}`);
      
      // Fetch well-known from this homeserver
      const wellKnown = await fetchWellKnownWithRTC(homeserver);
      const livekitFocus = getLiveKitFocus(wellKnown);
      
      if (!livekitFocus) {
        console.log(`No LiveKit config on ${homeserver}, trying next...`);
        continue;
      }
      
      // Always get OpenID token from user's own homeserver
      // (Can't use access token on remote homeserver)
      const openIdToken = await requestOpenIdToken(userHomeserver, userId, accessToken);
      
      // Exchange for LiveKit JWT
      const jwt = await fetchLiveKitJWT(
        livekitFocus.livekit_service_url,
        roomId,
        userId,
        deviceId,
        openIdToken
      );
      
      console.log(`Successfully got LiveKit JWT from ${homeserver}`);
      return { jwt, homeserver };
    } catch (error) {
      console.warn(`Failed to get LiveKit JWT from ${homeserver}:`, error);
      // Continue to next homeserver
    }
  }
  
  console.error('Failed to get LiveKit JWT from any homeserver');
  return null;
}

/**
 * Hook to get RTC configuration functions
 * Provides utilities for fetching well-known data and LiveKit JWTs
 */
export function useRtcConfig() {
  const mx = useMatrixClient();

  const getWellKnown = useCallback(async (): Promise<WellKnownWithRTC> => {
    const baseUrl = mx.getHomeserverUrl();
    return fetchWellKnownWithRTC(baseUrl);
  }, [mx]);

  const getLiveKitJWT = useCallback(
    async (roomId: string): Promise<LiveKitJWTResponse | null> => {
      const baseUrl = mx.getHomeserverUrl();
      const accessToken = mx.getAccessToken();
      const deviceId = mx.getDeviceId();
      const userId = mx.getSafeUserId();

      if (!accessToken || !deviceId) {
        throw new Error('Missing access token or device ID');
      }

      const wellKnown = await fetchWellKnownWithRTC(baseUrl);
      const livekitFocus = getLiveKitFocus(wellKnown);

      if (!livekitFocus) {
        return null;
      }

      // Get OpenID token from homeserver
      const openIdToken = await requestOpenIdToken(baseUrl, userId, accessToken);

      // Exchange for LiveKit JWT
      return fetchLiveKitJWT(livekitFocus.livekit_service_url, roomId, userId, deviceId, openIdToken);
    },
    [mx]
  );

  const isCallSupported = useCallback(async (): Promise<boolean> => {
    try {
      const baseUrl = mx.getHomeserverUrl();
      const wellKnown = await fetchWellKnownWithRTC(baseUrl);
      return getLiveKitFocus(wellKnown) !== undefined;
    } catch {
      return false;
    }
  }, [mx]);

  return {
    getWellKnown,
    getLiveKitJWT,
    isCallSupported,
  };
}
