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 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
   */
  async authenticateWebSocket(
    request: FastifyRequest
  ): Promise<AuthContext | null> {
    try {
      const token = this.extractBearerToken(request);
      if (!token) {
        this.config.logger.warn('No bearer token in WebSocket connection');
        return null;
      }

      const userId = await this.config.userService.verifyWebToken(token);
      if (!userId) {
        this.config.logger.warn('Invalid JWT token');
        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 (may take time if creating new container)
      this.config.logger.info({ userId }, 'Ensuring user container is running');
      const { mcpEndpoint, wasCreated } = await this.config.containerManager.ensureContainerRunning(
        userId,
        license
      );

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

      // Update license with actual MCP endpoint
      license.mcpServerUrl = mcpEndpoint;

      const sessionId = `ws_${userId}_${Date.now()}`;

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

  /**
   * 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
      );

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

      // Update license with actual MCP endpoint
      license.mcpServerUrl = mcpEndpoint;

      const sessionId = `tg_${telegramUserId}_${Date.now()}`;

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

  /**
   * Extract bearer token from request headers
   */
  private extractBearerToken(request: FastifyRequest): string | null {
    const auth = request.headers.authorization;
    if (!auth || !auth.startsWith('Bearer ')) {
      return null;
    }
    return auth.substring(7);
  }
}
