/**
 * CSRF Session Manager
 * Manages CSRF sessions and token storage
 */

import type { CSRFConfig, CSRFSession, CSRFToken } from './types';

export class CSRFSessionManager {
  private sessions: Map<string, CSRFSession> = new Map();
  private config: Required<CSRFConfig>;
  private cleanupInterval: NodeJS.Timeout | null = null;

  constructor(config: Required<CSRFConfig>) {
    this.config = config;
    this.startCleanupTimer();
  }

  /**
   * Create a new CSRF session
   */
  createSession(sessionId: string): CSRFSession {
    const session: CSRFSession = {
      id: sessionId,
      tokens: new Map(),
      createdAt: Date.now(),
      lastActivity: Date.now()
    };

    this.sessions.set(sessionId, session);
    return session;
  }

  /**
   * Get an existing session or create a new one
   */
  getOrCreateSession(sessionId: string): CSRFSession {
    let session = this.sessions.get(sessionId);

    if (!session) {
      session = this.createSession(sessionId);
    } else {
      // Update last activity
      session.lastActivity = Date.now();
    }

    return session;
  }

  /**
   * Add a token to a session
   */
  addToken(sessionId: string, token: CSRFToken): void {
    const session = this.getOrCreateSession(sessionId);
    session.tokens.set(token.value, token);

    // Clean up expired tokens for this session
    this.cleanupSessionTokens(session);
  }

  /**
   * Validate a token for a session
   */
  validateSessionToken(sessionId: string, tokenValue: string): { valid: boolean; error?: string; expired?: boolean } {
    const session = this.sessions.get(sessionId);

    if (!session) {
      return { valid: false, error: 'Session not found' };
    }

    const token = session.tokens.get(tokenValue);

    if (!token) {
      return { valid: false, error: 'Token not found in session' };
    }

    // Check if token has expired
    if (Date.now() > token.expiresAt) {
      // Remove expired token
      session.tokens.delete(tokenValue);
      return { valid: false, error: 'Token expired', expired: true };
    }

    // Update session activity
    session.lastActivity = Date.now();

    return { valid: true };
  }

  /**
   * Remove a token from a session (for one-time use tokens)
   */
  consumeToken(sessionId: string, tokenValue: string): boolean {
    const session = this.sessions.get(sessionId);

    if (!session) {
      return false;
    }

    return session.tokens.delete(tokenValue);
  }

  /**
   * Get all active tokens for a session
   */
  getSessionTokens(sessionId: string): CSRFToken[] {
    const session = this.sessions.get(sessionId);

    if (!session) {
      return [];
    }

    // Clean up expired tokens first
    this.cleanupSessionTokens(session);

    return Array.from(session.tokens.values());
  }

  /**
   * Remove a session and all its tokens
   */
  removeSession(sessionId: string): boolean {
    return this.sessions.delete(sessionId);
  }

  /**
   * Clean up expired tokens for a specific session
   */
  private cleanupSessionTokens(session: CSRFSession): void {
    const now = Date.now();
    const expiredTokens: string[] = [];

    for (const [tokenValue, token] of session.tokens) {
      if (now > token.expiresAt) {
        expiredTokens.push(tokenValue);
      }
    }

    expiredTokens.forEach(tokenValue => {
      session.tokens.delete(tokenValue);
    });
  }

  /**
   * Clean up expired sessions and tokens
   */
  private cleanup(): void {
    const now = Date.now();
    const sessionTimeout = this.config.tokenExpiry * 2; // Sessions expire after 2x token expiry
    const expiredSessions: string[] = [];

    for (const [sessionId, session] of this.sessions) {
      // Remove sessions that haven't been active for too long
      if (now - session.lastActivity > sessionTimeout) {
        expiredSessions.push(sessionId);
        continue;
      }

      // Clean up expired tokens in active sessions
      this.cleanupSessionTokens(session);
    }

    // Remove expired sessions
    expiredSessions.forEach(sessionId => {
      this.sessions.delete(sessionId);
    });
  }

  /**
   * Start automatic cleanup timer
   */
  private startCleanupTimer(): void {
    // Run cleanup every 15 minutes
    this.cleanupInterval = setInterval(() => {
      this.cleanup();
    }, 15 * 60 * 1000);
  }

  /**
   * Stop cleanup timer
   */
  destroy(): void {
    if (this.cleanupInterval) {
      clearInterval(this.cleanupInterval);
      this.cleanupInterval = null;
    }
    this.sessions.clear();
  }

  /**
   * Get session statistics
   */
  getStats(): {
    totalSessions: number;
    totalTokens: number;
    activeSessions: number;
  } {
    let totalTokens = 0;
    let activeSessions = 0;
    const now = Date.now();
    const sessionTimeout = this.config.tokenExpiry * 2;

    for (const session of this.sessions.values()) {
      totalTokens += session.tokens.size;

      if (now - session.lastActivity <= sessionTimeout) {
        activeSessions++;
      }
    }

    return {
      totalSessions: this.sessions.size,
      totalTokens,
      activeSessions
    };
  }
}
