/**
 * Connection Service
 *
 * Client-side API for user networking features:
 * - User search
 * - Connection management (follow/unfollow)
 * - Edge metadata (labels, notes, colors)
 * - Network graph retrieval
 */

import type {
  UserProfile,
  UserSearchResult,
  Connection,
  ConnectionWithMetadata,
  EdgeMetadata,
  NetworkGraph,
  GraphNode,
  GraphEdge,
  TrustLevel,
} from './types';
import { WORKER_URL } from '../../constants/workerUrl';

// =============================================================================
// Configuration
// =============================================================================

const API_BASE = `${WORKER_URL}/api/networking`;

// =============================================================================
// Helper Functions
// =============================================================================

/**
 * Get the current user's CryptID username from localStorage session
 */
function getCurrentUserId(): string | null {
  try {
    // Session is stored as 'canvas_auth_session' by sessionPersistence.ts
    const sessionStr = localStorage.getItem('canvas_auth_session');
    if (sessionStr) {
      const session = JSON.parse(sessionStr);
      if (session.authed && session.username) {
        return session.username;
      }
    }
  } catch {
    // Ignore parsing errors
  }
  return null;
}

async function fetchJson<T>(url: string, options?: RequestInit): Promise<T> {
  // Get the current user ID for authentication
  const userId = getCurrentUserId();

  const headers: Record<string, string> = {
    'Content-Type': 'application/json',
    ...(options?.headers as Record<string, string> || {}),
  };

  // Add user ID header for authentication
  if (userId) {
    headers['X-User-Id'] = userId;
  }

  const response = await fetch(url, {
    ...options,
    headers,
  });

  // Check content type to ensure we're getting JSON
  const contentType = response.headers.get('content-type') || '';
  const isJson = contentType.includes('application/json');

  if (!response.ok) {
    if (isJson) {
      const errorData = await response.json().catch(() => ({ message: response.statusText })) as { message?: string };
      throw new Error(errorData.message || `HTTP ${response.status}`);
    }
    // If we got HTML (like a 404 page), throw a more descriptive error
    throw new Error(`API unavailable (HTTP ${response.status}). Is the worker running?`);
  }

  // Ensure we're getting JSON before parsing
  if (!isJson) {
    throw new Error('API returned non-JSON response. Is the worker running on port 5172?');
  }

  return response.json();
}

function generateId(): string {
  return crypto.randomUUID();
}

// =============================================================================
// User Search
// =============================================================================

/**
 * Search for users by username or display name
 */
export async function searchUsers(
  query: string,
  limit: number = 20
): Promise<UserSearchResult[]> {
  const params = new URLSearchParams({ q: query, limit: String(limit) });
  return fetchJson<UserSearchResult[]>(`${API_BASE}/users/search?${params}`);
}

/**
 * Get a user's public profile
 */
export async function getUserProfile(userId: string): Promise<UserProfile | null> {
  try {
    return await fetchJson<UserProfile>(`${API_BASE}/users/${userId}`);
  } catch (error) {
    if ((error as Error).message.includes('404')) {
      return null;
    }
    throw error;
  }
}

/**
 * Update current user's profile
 */
export async function updateMyProfile(updates: Partial<{
  displayName: string;
  bio: string;
  avatarColor: string;
}>): Promise<UserProfile> {
  return fetchJson<UserProfile>(`${API_BASE}/users/me`, {
    method: 'PUT',
    body: JSON.stringify(updates),
  });
}

// =============================================================================
// Connection Management
// =============================================================================

/**
 * Create a connection (follow a user)
 * @param toUserId - The user to connect to
 * @param trustLevel - 'connected' (yellow, view) or 'trusted' (green, edit)
 */
export async function createConnection(
  toUserId: string,
  trustLevel: TrustLevel = 'connected'
): Promise<Connection> {
  return fetchJson<Connection>(`${API_BASE}/connections`, {
    method: 'POST',
    body: JSON.stringify({ toUserId, trustLevel }),
  });
}

/**
 * Update trust level for an existing connection
 */
export async function updateTrustLevel(
  connectionId: string,
  trustLevel: TrustLevel
): Promise<Connection> {
  return fetchJson<Connection>(`${API_BASE}/connections/${connectionId}/trust`, {
    method: 'PUT',
    body: JSON.stringify({ trustLevel }),
  });
}

/**
 * Remove a connection (unfollow a user)
 */
export async function removeConnection(connectionId: string): Promise<void> {
  await fetch(`${API_BASE}/connections/${connectionId}`, {
    method: 'DELETE',
  });
}

/**
 * Get a specific connection by ID
 */
export async function getConnection(connectionId: string): Promise<ConnectionWithMetadata | null> {
  try {
    return await fetchJson<ConnectionWithMetadata>(`${API_BASE}/connections/${connectionId}`);
  } catch (error) {
    if ((error as Error).message.includes('404')) {
      return null;
    }
    throw error;
  }
}

/**
 * Get all connections for current user
 */
export async function getMyConnections(): Promise<ConnectionWithMetadata[]> {
  return fetchJson<ConnectionWithMetadata[]>(`${API_BASE}/connections`);
}

/**
 * Get users who are connected to current user (followers)
 */
export async function getMyFollowers(): Promise<Connection[]> {
  return fetchJson<Connection[]>(`${API_BASE}/connections/followers`);
}

/**
 * Check if current user is connected to a specific user
 */
export async function isConnectedTo(userId: string): Promise<boolean> {
  try {
    const result = await fetchJson<{ connected: boolean }>(
      `${API_BASE}/connections/check/${userId}`
    );
    return result.connected;
  } catch {
    return false;
  }
}

// =============================================================================
// Edge Metadata
// =============================================================================

/**
 * Update metadata for a connection edge
 */
export async function updateEdgeMetadata(
  connectionId: string,
  metadata: Partial<EdgeMetadata>
): Promise<EdgeMetadata> {
  return fetchJson<EdgeMetadata>(`${API_BASE}/connections/${connectionId}/metadata`, {
    method: 'PUT',
    body: JSON.stringify(metadata),
  });
}

/**
 * Get metadata for a connection edge
 */
export async function getEdgeMetadata(connectionId: string): Promise<EdgeMetadata | null> {
  try {
    return await fetchJson<EdgeMetadata>(`${API_BASE}/connections/${connectionId}/metadata`);
  } catch (error) {
    if ((error as Error).message.includes('404')) {
      return null;
    }
    throw error;
  }
}

// =============================================================================
// Network Graph
// =============================================================================

/**
 * Transform API edge format to client format
 * API uses fromUserId/toUserId, client uses source/target for d3
 */
function transformEdge(edge: any): GraphEdge {
  return {
    id: edge.id,
    source: edge.fromUserId || edge.source,
    target: edge.toUserId || edge.target,
    trustLevel: edge.trustLevel,
    isMutual: edge.isMutual,
    effectiveTrustLevel: edge.effectiveTrustLevel,
    metadata: edge.metadata,
    isVisible: true,
  };
}

/**
 * Get the full network graph for current user
 */
export async function getMyNetworkGraph(): Promise<NetworkGraph> {
  const response = await fetchJson<any>(`${API_BASE}/graph`);
  return {
    nodes: response.nodes || [],
    edges: (response.edges || []).map(transformEdge),
    myConnections: response.myConnections || [],
  };
}

/**
 * Get network graph scoped to room participants
 * Returns full network in grey, room participants colored
 */
export async function getRoomNetworkGraph(
  roomParticipants: string[]
): Promise<NetworkGraph> {
  const response = await fetchJson<any>(`${API_BASE}/graph/room`, {
    method: 'POST',
    body: JSON.stringify({ participants: roomParticipants }),
  });
  return {
    nodes: response.nodes || [],
    edges: (response.edges || []).map(transformEdge),
    myConnections: response.myConnections || [],
  };
}

/**
 * Get mutual connections between current user and another user
 */
export async function getMutualConnections(userId: string): Promise<UserProfile[]> {
  return fetchJson<UserProfile[]>(`${API_BASE}/connections/mutual/${userId}`);
}

// =============================================================================
// Graph Building Helpers (Client-side)
// =============================================================================

/**
 * Build a GraphNode from a UserProfile and room state
 */
export function buildGraphNode(
  profile: UserProfile,
  options: {
    isInRoom: boolean;
    roomPresenceColor?: string;
    isCurrentUser: boolean;
  }
): GraphNode {
  return {
    id: profile.id,
    username: profile.username,
    displayName: profile.displayName,
    avatarColor: profile.avatarColor,
    isInRoom: options.isInRoom,
    roomPresenceColor: options.roomPresenceColor,
    isCurrentUser: options.isCurrentUser,
    isAnonymous: false, // Users with profiles are authenticated
  };
}

/**
 * Build a GraphEdge from a Connection
 */
export function buildGraphEdge(
  connection: ConnectionWithMetadata,
  currentUserId: string
): GraphEdge {
  const isOnEdge = connection.fromUserId === currentUserId || connection.toUserId === currentUserId;

  return {
    id: connection.id,
    source: connection.fromUserId,
    target: connection.toUserId,
    trustLevel: connection.trustLevel,
    effectiveTrustLevel: connection.effectiveTrustLevel,
    isMutual: connection.isMutual,
    metadata: isOnEdge ? connection.metadata : undefined,
    isVisible: true,
  };
}

// =============================================================================
// Local Storage Cache (for offline/fast loading)
// =============================================================================

const CACHE_KEY = 'network_graph_cache';
const CACHE_TTL = 5 * 60 * 1000; // 5 minutes

interface CachedGraph {
  graph: NetworkGraph;
  timestamp: number;
}

export function getCachedGraph(): NetworkGraph | null {
  try {
    const cached = localStorage.getItem(CACHE_KEY);
    if (!cached) return null;

    const { graph, timestamp }: CachedGraph = JSON.parse(cached);
    if (Date.now() - timestamp > CACHE_TTL) {
      localStorage.removeItem(CACHE_KEY);
      return null;
    }

    return graph;
  } catch {
    return null;
  }
}

export function setCachedGraph(graph: NetworkGraph): void {
  try {
    const cached: CachedGraph = {
      graph,
      timestamp: Date.now(),
    };
    localStorage.setItem(CACHE_KEY, JSON.stringify(cached));
  } catch {
    // Ignore storage errors
  }
}

export function clearGraphCache(): void {
  try {
    localStorage.removeItem(CACHE_KEY);
  } catch {
    // Ignore
  }
}
