/**
 * JWT Token Management Service
 *
 * Handles generation and verification of access and refresh tokens
 */

import jwt, { SignOptions } from 'jsonwebtoken';
import { config } from '../config';
import { logger } from '../utils/logger';

// JWT Payload Types
export interface JWTPayload {
  userId: string;
  email: string;
  plan: string;
  subscriptionStatus: string;
  type: 'access';
}

export interface JWTRefreshPayload {
  userId: string;
  type: 'refresh';
}

/**
 * Generate access token (short-lived)
 * Default expiry: 15 minutes
 */
export function generateAccessToken(
  userId: string,
  email: string,
  plan: string,
  subscriptionStatus: string
): string {
  try {
    const payload: JWTPayload = {
      userId,
      email,
      plan,
      subscriptionStatus,
      type: 'access',
    };

    const options: SignOptions = {
      expiresIn: config.jwtAccessExpiry as any,
      issuer: 'translate.press.zone',
      audience: 'api',
    };
    const token = jwt.sign(payload, config.jwtAccessSecret, options);

    logger.debug('Access token generated', { userId, email });
    return token;
  } catch (error) {
    logger.error('Failed to generate access token', { error, userId });
    throw new Error('Failed to generate access token');
  }
}

/**
 * Generate refresh token (long-lived)
 * Default expiry: 7 days
 */
export function generateRefreshToken(userId: string): string {
  try {
    const payload: JWTRefreshPayload = {
      userId,
      type: 'refresh',
    };

    const options: SignOptions = {
      expiresIn: config.jwtRefreshExpiry as any,
      issuer: 'translate.press.zone',
      audience: 'api',
    };
    const token = jwt.sign(payload, config.jwtRefreshSecret, options);

    logger.debug('Refresh token generated', { userId });
    return token;
  } catch (error) {
    logger.error('Failed to generate refresh token', { error, userId });
    throw new Error('Failed to generate refresh token');
  }
}

/**
 * Verify and decode access token
 * @throws Error if token is invalid or expired
 */
export function verifyAccessToken(token: string): JWTPayload {
  try {
    const decoded = jwt.verify(token, config.jwtAccessSecret, {
      issuer: 'translate.press.zone',
      audience: 'api',
    }) as JWTPayload;

    // Validate token type
    if (decoded.type !== 'access') {
      throw new Error('Invalid token type');
    }

    logger.debug('Access token verified', { userId: decoded.userId });
    return decoded;
  } catch (error) {
    if (error instanceof jwt.TokenExpiredError) {
      logger.debug('Access token expired');
      throw new Error('Access token expired');
    }
    if (error instanceof jwt.JsonWebTokenError) {
      logger.warn('Invalid access token', { error: error.message });
      throw new Error('Invalid access token');
    }
    logger.error('Failed to verify access token', { error });
    throw new Error('Failed to verify access token');
  }
}

/**
 * Verify and decode refresh token
 * @throws Error if token is invalid or expired
 */
export function verifyRefreshToken(token: string): JWTRefreshPayload {
  try {
    const decoded = jwt.verify(token, config.jwtRefreshSecret, {
      issuer: 'translate.press.zone',
      audience: 'api',
    }) as JWTRefreshPayload;

    // Validate token type
    if (decoded.type !== 'refresh') {
      throw new Error('Invalid token type');
    }

    logger.debug('Refresh token verified', { userId: decoded.userId });
    return decoded;
  } catch (error) {
    if (error instanceof jwt.TokenExpiredError) {
      logger.debug('Refresh token expired');
      throw new Error('Refresh token expired');
    }
    if (error instanceof jwt.JsonWebTokenError) {
      logger.warn('Invalid refresh token', { error: error.message });
      throw new Error('Invalid refresh token');
    }
    logger.error('Failed to verify refresh token', { error });
    throw new Error('Failed to verify refresh token');
  }
}

/**
 * Extract token from Authorization header
 * Expected format: "Bearer {token}"
 */
export function extractTokenFromHeader(authHeader: string | undefined): string | null {
  if (!authHeader) {
    return null;
  }

  const parts = authHeader.split(' ');
  if (parts.length !== 2 || parts[0] !== 'Bearer') {
    return null;
  }

  return parts[1];
}
