import type { IncomingMessage } from 'http';

export interface AuthConfig {
  /** Static bearer token. If set, all connections must provide it. */
  token?: string;
  /** Trust Tailscale-User-Login header for identity. */
  tailscaleIdentity?: boolean;
}

export interface AuthResult {
  authenticated: boolean;
  identity?: string;
  error?: string;
}

/**
 * Authenticates a WebSocket upgrade request or HTTP request.
 *
 * Auth is checked in this order:
 * 1. If token is configured, validate via:
 *    a. Authorization: Bearer <token> header
 *    b. ?token=<token> query parameter (useful for browser WebSocket clients)
 * 2. If tailscaleIdentity is enabled, extract identity from Tailscale-User-Login header
 * 3. If no auth is configured, allow all connections
 */
export function authenticateRequest(req: IncomingMessage, config: AuthConfig): AuthResult {
  // If token auth is configured, it's required
  if (config.token) {
    let tokenValid = false;

    // Check Authorization header first
    const authHeader = req.headers['authorization'];
    if (authHeader) {
      const parts = authHeader.split(' ');
      if (parts.length !== 2 || parts[0] !== 'Bearer') {
        return { authenticated: false, error: 'Invalid Authorization format (expected: Bearer <token>)' };
      }
      tokenValid = parts[1] === config.token;
    } else {
      // Check query parameter as fallback (useful for WebSocket from browsers)
      const queryToken = extractQueryToken(req);
      if (queryToken !== undefined) {
        tokenValid = queryToken === config.token;
      } else {
        return { authenticated: false, error: 'Authorization required (Bearer token header or ?token= query parameter)' };
      }
    }

    if (!tokenValid) {
      return { authenticated: false, error: 'Invalid token' };
    }

    // Token is valid — check for Tailscale identity too
    const identity = extractTailscaleIdentity(req, config);
    return { authenticated: true, identity: identity ?? 'token-user' };
  }

  // If Tailscale identity is configured (no token), use it
  if (config.tailscaleIdentity) {
    const identity = extractTailscaleIdentity(req, config);
    if (identity) {
      return { authenticated: true, identity };
    }
    // Tailscale identity configured but header not present — still allow (might be local)
    return { authenticated: true, identity: 'anonymous' };
  }

  // No auth configured — allow all
  return { authenticated: true, identity: 'anonymous' };
}

function extractQueryToken(req: IncomingMessage): string | undefined {
  try {
    const url = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
    const token = url.searchParams.get('token');
    return token ?? undefined;
  } catch {
    return undefined;
  }
}

function extractTailscaleIdentity(req: IncomingMessage, config: AuthConfig): string | undefined {
  if (!config.tailscaleIdentity) {return undefined;}
  const header = req.headers['tailscale-user-login'];
  if (typeof header === 'string' && header.length > 0) {
    return header;
  }
  return undefined;
}
