/**
 * Authentication and Session Management
 * JWT-based authentication for BaoLife.
 * Ported from Python auth.py
 */

import crypto from 'crypto';
import jwt from 'jsonwebtoken';
import { config } from '../config.js';
import { getConnection } from '../database/pool.js';

// ============================================================================
// Types
// ============================================================================

export interface TokenPayload {
  user_id: string;
  iat: number;
  exp: number;
  jti: string;
  [key: string]: unknown;
}

export interface SessionInfo {
  token: string;
  user_id: string;
  expires_in: number;
}

export class AuthError extends Error {
  constructor(message: string) {
    super(message);
    this.name = 'AuthError';
  }
}

// ============================================================================
// Auth Manager
// ============================================================================

export class AuthManager {
  private secret: string;
  private timeout: number;
  private algorithm: jwt.Algorithm = 'HS256';

  constructor() {
    this.secret = config.JWT_SECRET || 'default-secret-change-me';
    this.timeout = config.SESSION_TIMEOUT || 86400; // 24 hours default
  }

  /**
   * Hash password using SHA-256
   */
  hashPassword(password: string): string {
    return crypto.createHash('sha256').update(password).digest('hex');
  }

  /**
   * Verify password against hash
   */
  verifyPassword(password: string, hashed: string): boolean {
    try {
      return this.hashPassword(password) === hashed;
    } catch (error) {
      console.error('Password verification error:', error);
      return false;
    }
  }

  /**
   * Create JWT token for user
   */
  createToken(userId: string, additionalClaims?: Record<string, unknown>): string {
    const now = Math.floor(Date.now() / 1000);
    const payload: TokenPayload = {
      user_id: userId,
      iat: now,
      exp: now + this.timeout,
      jti: crypto.randomBytes(16).toString('base64url'),
      ...additionalClaims,
    };

    return jwt.sign(payload, this.secret, { algorithm: this.algorithm });
  }

  /**
   * Verify JWT token
   */
  verifyToken(token: string): TokenPayload {
    try {
      const payload = jwt.verify(token, this.secret, {
        algorithms: [this.algorithm],
      }) as TokenPayload;
      return payload;
    } catch (error) {
      if (error instanceof jwt.TokenExpiredError) {
        throw new AuthError('Token expired');
      }
      throw new AuthError(`Invalid token: ${error}`);
    }
  }

  /**
   * Create authenticated session
   */
  createSession(userId: string): SessionInfo {
    const token = this.createToken(userId);
    return {
      token,
      user_id: userId,
      expires_in: this.timeout,
    };
  }

  /**
   * Authenticate user with credentials
   */
  async authenticateUser(
    userId: string,
    password: string
  ): Promise<SessionInfo | null> {
    let connection;
    try {
      connection = await getConnection();
      const [rows] = await connection.execute(
        'SELECT userID, passwordHash FROM users WHERE userID = ?',
        [userId]
      );

      const results = rows as Array<{ userID: string; passwordHash: string }>;
      if (!results.length) {
        console.log(`Authentication failed: user ${userId} not found`);
        return null;
      }

      const { passwordHash } = results[0];

      if (!this.verifyPassword(password, passwordHash)) {
        console.log(`Authentication failed: invalid password for ${userId}`);
        return null;
      }

      const session = this.createSession(userId);
      console.log(`User ${userId} authenticated successfully`);
      return session;
    } catch (error) {
      console.error('Authentication error:', error);
      return null;
    } finally {
      if (connection) {
        connection.release();
      }
    }
  }
}

// ============================================================================
// Singleton and Helpers
// ============================================================================

export const authManager = new AuthManager();

/**
 * Require authentication for operation
 */
export function requireAuth(token: string): string | null {
  try {
    const payload = authManager.verifyToken(token);
    const userId = payload.user_id;
    console.log(`Authenticated user: ${userId}`);
    return userId;
  } catch (error) {
    if (error instanceof AuthError) {
      console.log(`Authentication failed: ${error.message}`);
    }
    return null;
  }
}

/**
 * Middleware-style auth check
 */
export function isAuthenticated(token: string): boolean {
  return requireAuth(token) !== null;
}

// ============================================================================
// Export
// ============================================================================

export const auth = {
  AuthManager,
  AuthError,
  authManager,
  requireAuth,
  isAuthenticated,
};
