import { Logger } from 'pino';
import { randomUUID } from 'crypto';

export interface UserToken {
  userId: string;
  tokenId: string;
  createdAt: number;
  expiresAt: number;
  metadata: {
    userAgent?: string;
    ipAddress?: string;
    [key: string]: any;
  };
}

export interface AuthResult {
  success: boolean;
  userId?: string;
  sessionId?: string;
  token?: string;
  error?: string;
}

export class UserAuthManager {
  private logger: Logger;
  private userTokens: Map<string, UserToken> = new Map(); // tokenId -> UserToken
  private userSessions: Map<string, Set<string>> = new Map(); // userId -> Set<tokenId>
  private tokenCleanupInterval: NodeJS.Timeout;

  constructor(logger: Logger) {
    this.logger = logger;
    
    // Start token cleanup interval
    this.tokenCleanupInterval = setInterval(() => {
      this.cleanupExpiredTokens();
    }, 60000); // Check every minute
  }

  /**
   * Generate a new user authentication token
   */
  generateUserToken(metadata: any = {}): AuthResult {
    const userId = `user_${randomUUID()}`;
    const tokenId = `token_${randomUUID()}`;
    const now = Date.now();
    const expiresAt = now + (24 * 60 * 60 * 1000); // 24 hours

    const userToken: UserToken = {
      userId,
      tokenId,
      createdAt: now,
      expiresAt,
      metadata: {
        userAgent: metadata.userAgent,
        ipAddress: metadata.ipAddress,
        ...metadata
      }
    };

    // Store token
    this.userTokens.set(tokenId, userToken);

    // Track user sessions
    if (!this.userSessions.has(userId)) {
      this.userSessions.set(userId, new Set());
    }
    this.userSessions.get(userId)!.add(tokenId);

    this.logger.info(`Generated user token - User: ${userId}, Token: ${tokenId}`);

    return {
      success: true,
      userId,
      token: tokenId,
      sessionId: `session_${userId}_${Date.now()}`
    };
  }

  /**
   * Validate a user token
   */
  validateToken(tokenId: string): AuthResult {
    const userToken = this.userTokens.get(tokenId);
    
    if (!userToken) {
      return {
        success: false,
        error: 'Invalid token'
      };
    }

    // Check if token is expired
    if (Date.now() > userToken.expiresAt) {
      this.revokeToken(tokenId);
      return {
        success: false,
        error: 'Token expired'
      };
    }

    return {
      success: true,
      userId: userToken.userId,
      sessionId: `session_${userToken.userId}_${userToken.createdAt}`
    };
  }

  /**
   * Refresh a user token (extend expiration)
   */
  refreshToken(tokenId: string): AuthResult {
    const userToken = this.userTokens.get(tokenId);
    
    if (!userToken) {
      return {
        success: false,
        error: 'Invalid token'
      };
    }

    // Extend expiration by 24 hours
    userToken.expiresAt = Date.now() + (24 * 60 * 60 * 1000);
    
    this.logger.info(`Refreshed token: ${tokenId} for user: ${userToken.userId}`);

    return {
      success: true,
      userId: userToken.userId,
      token: tokenId,
      sessionId: `session_${userToken.userId}_${userToken.createdAt}`
    };
  }

  /**
   * Revoke a user token
   */
  revokeToken(tokenId: string): boolean {
    const userToken = this.userTokens.get(tokenId);
    
    if (!userToken) {
      return false;
    }

    // Remove from user sessions
    const userSessions = this.userSessions.get(userToken.userId);
    if (userSessions) {
      userSessions.delete(tokenId);
      if (userSessions.size === 0) {
        this.userSessions.delete(userToken.userId);
      }
    }

    // Remove token
    this.userTokens.delete(tokenId);
    
    this.logger.info(`Revoked token: ${tokenId} for user: ${userToken.userId}`);
    return true;
  }

  /**
   * Revoke all tokens for a user
   */
  revokeUserTokens(userId: string): number {
    const userSessions = this.userSessions.get(userId);
    
    if (!userSessions) {
      return 0;
    }

    let revokedCount = 0;
    for (const tokenId of userSessions) {
      if (this.userTokens.delete(tokenId)) {
        revokedCount++;
      }
    }

    this.userSessions.delete(userId);
    
    this.logger.info(`Revoked ${revokedCount} tokens for user: ${userId}`);
    return revokedCount;
  }

  /**
   * Get user information by token
   */
  getUserInfo(tokenId: string): UserToken | null {
    return this.userTokens.get(tokenId) || null;
  }

  /**
   * Get all active tokens for a user
   */
  getUserTokens(userId: string): UserToken[] {
    const userSessions = this.userSessions.get(userId);
    
    if (!userSessions) {
      return [];
    }

    const tokens: UserToken[] = [];
    for (const tokenId of userSessions) {
      const token = this.userTokens.get(tokenId);
      if (token) {
        tokens.push(token);
      }
    }

    return tokens;
  }

  /**
   * Extract user ID from session ID
   */
  extractUserIdFromSession(sessionId: string): string | null {
    // Session format: session_{userId}_{timestamp}
    const match = sessionId.match(/^session_(.+?)_\d+$/);
    return match ? match[1] : null;
  }

  /**
   * Create anonymous user session (no token required)
   */
  createAnonymousSession(metadata: any = {}): AuthResult {
    const userId = `anon_${randomUUID()}`;
    const sessionId = `session_${userId}_${Date.now()}`;

    this.logger.info(`Created anonymous session - User: ${userId}, Session: ${sessionId}`);

    return {
      success: true,
      userId,
      sessionId
    };
  }

  /**
   * Clean up expired tokens
   */
  private cleanupExpiredTokens(): void {
    const now = Date.now();
    const tokensToRemove: string[] = [];

    for (const [tokenId, userToken] of this.userTokens.entries()) {
      if (now > userToken.expiresAt) {
        tokensToRemove.push(tokenId);
      }
    }

    for (const tokenId of tokensToRemove) {
      this.revokeToken(tokenId);
    }

    if (tokensToRemove.length > 0) {
      this.logger.info(`Cleaned up ${tokensToRemove.length} expired tokens`);
    }
  }

  /**
   * Get authentication statistics
   */
  getAuthStats(): any {
    return {
      totalTokens: this.userTokens.size,
      totalUsers: this.userSessions.size,
      activeTokens: Array.from(this.userTokens.values()).filter(token => Date.now() < token.expiresAt).length
    };
  }

  /**
   * Authenticate request from headers
   */
  authenticateRequest(headers: any): AuthResult {
    // Try to get token from Authorization header
    const authHeader = headers.authorization || headers.Authorization;
    if (authHeader && authHeader.startsWith('Bearer ')) {
      const token = authHeader.substring(7);
      return this.validateToken(token);
    }

    // Try to get token from custom header
    const tokenHeader = headers['x-auth-token'] || headers['X-Auth-Token'];
    if (tokenHeader) {
      return this.validateToken(tokenHeader);
    }

    // Create anonymous session if no token provided
    return this.createAnonymousSession({
      userAgent: headers['user-agent'],
      ipAddress: headers['x-forwarded-for'] || 'unknown'
    });
  }

  /**
   * Cleanup resources
   */
  destroy(): void {
    if (this.tokenCleanupInterval) {
      clearInterval(this.tokenCleanupInterval);
    }

    this.userTokens.clear();
    this.userSessions.clear();
  }
}
