import type { FastifyRequest, FastifyBaseLogger } from 'fastify';
import { UserService } from '../db/user-service.js';
import { ChannelType, type AuthContext } from '../types/user.js';
import type { ContainerManager } from '../k8s/container-manager.js';

export interface AuthResult {
  authContext: AuthContext | null;
  isSpinningUp: boolean;
}

export interface AuthenticatorConfig {
  userService: UserService;
  containerManager: ContainerManager;
  logger: FastifyBaseLogger;
}

/**
 * Multi-channel authenticator
 * Handles authentication for WebSocket, Telegram, and other channels
 */
export class Authenticator {
  private config: AuthenticatorConfig;

  constructor(config: AuthenticatorConfig) {
    this.config = config;
  }

  /**
   * Authenticate WebSocket connection via JWT token
   * Also ensures the user's container is running
   * Returns immediately if container is spinning up (non-blocking)
   */
  async authenticateWebSocket(
    request: FastifyRequest
  ): Promise<AuthResult> {
    try {
      const token = this.extractBearerToken(request);
      if (!token) {
        this.config.logger.warn('No bearer token in WebSocket connection');
        return { authContext: null, isSpinningUp: false };
      }

      const userId = await this.config.userService.verifyWebToken(token);
      if (!userId) {
        this.config.logger.warn('Invalid JWT token');
        return { authContext: null, isSpinningUp: false };
      }

      const license = await this.config.userService.getUserLicense(userId);
      if (!license) {
        this.config.logger.warn({ userId }, 'User license not found');
        return { authContext: null, isSpinningUp: false };
      }

      // Ensure container is running (non-blocking - returns immediately if creating new)
      this.config.logger.info({ userId }, 'Ensuring user container is running');
      const { mcpEndpoint, wasCreated, isSpinningUp } = await this.config.containerManager.ensureContainerRunning(
        userId,
        license.license,
        false // Don't wait for ready
      );

      if (isSpinningUp) {
        this.config.logger.info(
          { userId, wasCreated },
          'Container is spinning up'
        );
      } else {
        this.config.logger.info(
          { userId, mcpEndpoint, wasCreated },
          'Container is ready'
        );
      }

      const sessionId = `ws_${userId}`;

      return {
        authContext: {
          userId,
          channelType: ChannelType.WEBSOCKET,
          channelUserId: userId, // For WebSocket, same as userId
          sessionId,
          license: license.license,
          mcpServerUrl: mcpEndpoint,
          authenticatedAt: new Date(),
        },
        isSpinningUp,
      };
    } catch (error) {
      this.config.logger.error({ error }, 'WebSocket authentication error');
      return { authContext: null, isSpinningUp: false };
    }
  }

  /**
   * Authenticate Telegram webhook
   * Also ensures the user's container is running
   */
  async authenticateTelegram(telegramUserId: string): Promise<AuthContext | null> {
    try {
      const userId = await this.config.userService.getUserIdFromChannel(
        'telegram',
        telegramUserId
      );

      if (!userId) {
        this.config.logger.warn(
          { telegramUserId },
          'Telegram user not linked to platform user'
        );
        return null;
      }

      const license = await this.config.userService.getUserLicense(userId);
      if (!license) {
        this.config.logger.warn({ userId }, 'User license not found');
        return null;
      }

      // Ensure container is running
      this.config.logger.info({ userId }, 'Ensuring user container is running');
      const { mcpEndpoint, wasCreated } = await this.config.containerManager.ensureContainerRunning(
        userId,
        license.license
      );

      this.config.logger.info(
        { userId, mcpEndpoint, wasCreated },
        'Container is ready'
      );

      const sessionId = `tg_${telegramUserId}`;

      return {
        userId,
        channelType: ChannelType.TELEGRAM,
        channelUserId: telegramUserId,
        sessionId,
        license: license.license,
        mcpServerUrl: mcpEndpoint,
        authenticatedAt: new Date(),
      };
    } catch (error) {
      this.config.logger.error({ error }, 'Telegram authentication error');
      return null;
    }
  }

  /**
   * Extract bearer token from request headers or query parameters
   * WebSocket connections can't set custom headers in browsers, so we support token in query params
   */
  private extractBearerToken(request: FastifyRequest): string | null {
    // Try Authorization header first
    const auth = request.headers.authorization;
    if (auth && auth.startsWith('Bearer ')) {
      return auth.substring(7);
    }

    // Fall back to query parameter (for WebSocket connections)
    const query = request.query as { token?: string };
    if (query.token) {
      return query.token;
    }

    return null;
  }
}
