import jwt from 'jsonwebtoken';
import logger from './logger';

export interface JWTPayload {
  userId: string;
  email: string;
  role: string;
  iat?: number;
  exp?: number;
}

export const generateAccessToken = (payload: Omit<JWTPayload, 'iat' | 'exp'>): string => {
  const startTime = Date.now();
  
  logger.info('🎫 Generating access token', {
    userId: payload.userId,
    email: payload.email,
    role: payload.role,
    expiresIn: process.env.JWT_EXPIRES_IN || '15m'
  });

  try {
    const secret = process.env.JWT_SECRET || '';
    const expiresIn: number | string = process.env.JWT_EXPIRES_IN || '15m';
    const token = jwt.sign(payload, secret as any, { expiresIn } as any);

    const totalTime = Date.now() - startTime;
    
    logger.info('✅ Access token generated successfully', {
      userId: payload.userId,
      email: payload.email,
      role: payload.role,
      tokenLength: token.length,
      totalTime: `${totalTime}ms`
    });

    return token;
  } catch (error) {
    const totalTime = Date.now() - startTime;
    
    logger.error('❌ Error generating access token', {
      error: error instanceof Error ? error.message : 'Unknown error',
      userId: payload.userId,
      email: payload.email,
      role: payload.role,
      totalTime: `${totalTime}ms`,
      stack: error instanceof Error ? error.stack : undefined
    });
    throw error;
  }
};

export const generateRefreshToken = (payload: Omit<JWTPayload, 'iat' | 'exp'>): string => {
  const startTime = Date.now();
  
  logger.info('🔄 Generating refresh token', {
    userId: payload.userId,
    email: payload.email,
    role: payload.role,
    expiresIn: process.env.JWT_REFRESH_EXPIRES_IN || '7d'
  });

  try {
    const secret = process.env.JWT_REFRESH_SECRET || '';
    const expiresIn: number | string = process.env.JWT_REFRESH_EXPIRES_IN || '7d';
    const token = jwt.sign(payload, secret as any, { expiresIn } as any);

    const totalTime = Date.now() - startTime;
    
    logger.info('✅ Refresh token generated successfully', {
      userId: payload.userId,
      email: payload.email,
      role: payload.role,
      tokenLength: token.length,
      totalTime: `${totalTime}ms`
    });

    return token;
  } catch (error) {
    const totalTime = Date.now() - startTime;
    
    logger.error('❌ Error generating refresh token', {
      error: error instanceof Error ? error.message : 'Unknown error',
      userId: payload.userId,
      email: payload.email,
      role: payload.role,
      totalTime: `${totalTime}ms`,
      stack: error instanceof Error ? error.stack : undefined
    });
    throw error;
  }
};

export const verifyAccessToken = (token: string): JWTPayload => {
  const startTime = Date.now();
  
  logger.info('🔍 Verifying access token', {
    tokenLength: token.length
  });

  try {
    const decoded = jwt.verify(
      token,
      process.env.JWT_SECRET as string
    ) as JWTPayload;

    const totalTime = Date.now() - startTime;
    
    logger.info('✅ Access token verified successfully', {
      userId: decoded.userId,
      email: decoded.email,
      role: decoded.role,
      issuedAt: decoded.iat ? new Date(decoded.iat * 1000).toISOString() : 'unknown',
      expiresAt: decoded.exp ? new Date(decoded.exp * 1000).toISOString() : 'unknown',
      totalTime: `${totalTime}ms`
    });

    return decoded;
  } catch (error) {
    const totalTime = Date.now() - startTime;
    
    if (error instanceof jwt.JsonWebTokenError) {
      logger.warn('⚠️ Invalid access token', {
        error: error.message,
        tokenLength: token.length,
        totalTime: `${totalTime}ms`
      });
    } else if (error instanceof jwt.TokenExpiredError) {
      logger.warn('⚠️ Expired access token', {
        error: error.message,
        tokenLength: token.length,
        totalTime: `${totalTime}ms`
      });
    } else {
      logger.error('❌ Error verifying access token', {
        error: error instanceof Error ? error.message : 'Unknown error',
        tokenLength: token.length,
        totalTime: `${totalTime}ms`,
        stack: error instanceof Error ? error.stack : undefined
      });
    }
    throw error;
  }
};

export const verifyRefreshToken = (token: string): JWTPayload => {
  const startTime = Date.now();
  
  logger.info('🔍 Verifying refresh token', {
    tokenLength: token.length
  });

  try {
    const decoded = jwt.verify(
      token,
      process.env.JWT_REFRESH_SECRET as string
    ) as JWTPayload;

    const totalTime = Date.now() - startTime;
    
    logger.info('✅ Refresh token verified successfully', {
      userId: decoded.userId,
      email: decoded.email,
      role: decoded.role,
      issuedAt: decoded.iat ? new Date(decoded.iat * 1000).toISOString() : 'unknown',
      expiresAt: decoded.exp ? new Date(decoded.exp * 1000).toISOString() : 'unknown',
      totalTime: `${totalTime}ms`
    });

    return decoded;
  } catch (error) {
    const totalTime = Date.now() - startTime;
    
    if (error instanceof jwt.JsonWebTokenError) {
      logger.warn('⚠️ Invalid refresh token', {
        error: error.message,
        tokenLength: token.length,
        totalTime: `${totalTime}ms`
      });
    } else if (error instanceof jwt.TokenExpiredError) {
      logger.warn('⚠️ Expired refresh token', {
        error: error.message,
        tokenLength: token.length,
        totalTime: `${totalTime}ms`
      });
    } else {
      logger.error('❌ Error verifying refresh token', {
        error: error instanceof Error ? error.message : 'Unknown error',
        tokenLength: token.length,
        totalTime: `${totalTime}ms`,
        stack: error instanceof Error ? error.stack : undefined
      });
    }
    throw error;
  }
};

export const getTokenExpirationTime = (expiresIn: string): Date => {
  logger.info('⏰ Calculating token expiration time', {
    expiresIn
  });

  try {
    const now = new Date();
    const expirationTime = new Date(now.getTime() + parseJwtExpiration(expiresIn));
    
    logger.info('✅ Token expiration time calculated', {
      expiresIn,
      currentTime: now.toISOString(),
      expirationTime: expirationTime.toISOString(),
      durationMs: expirationTime.getTime() - now.getTime()
    });

    return expirationTime;
  } catch (error) {
    logger.error('❌ Error calculating token expiration time', {
      error: error instanceof Error ? error.message : 'Unknown error',
      expiresIn,
      stack: error instanceof Error ? error.stack : undefined
    });
    throw error;
  }
};

const parseJwtExpiration = (expiresIn: string): number => {
  const unit = expiresIn.slice(-1);
  const value = parseInt(expiresIn.slice(0, -1));

  switch (unit) {
    case 's':
      return value * 1000;
    case 'm':
      return value * 60 * 1000;
    case 'h':
      return value * 60 * 60 * 1000;
    case 'd':
      return value * 24 * 60 * 60 * 1000;
    default:
      throw new Error(`Invalid expiration format: ${expiresIn}`);
  }
};

export const isTokenExpired = (token: string): boolean => {
  try {
    const decoded = jwt.decode(token) as JWTPayload;
    if (!decoded || !decoded.exp) {
      return true;
    }
    
    const now = Math.floor(Date.now() / 1000);
    const isExpired = decoded.exp < now;
    
    logger.info('🔍 Checking token expiration', {
      tokenLength: token.length,
      issuedAt: decoded.iat ? new Date(decoded.iat * 1000).toISOString() : 'unknown',
      expiresAt: new Date(decoded.exp * 1000).toISOString(),
      currentTime: new Date(now * 1000).toISOString(),
      isExpired,
      timeRemaining: decoded.exp - now
    });
    
    return isExpired;
  } catch (error) {
    logger.error('❌ Error checking token expiration', {
      error: error instanceof Error ? error.message : 'Unknown error',
      tokenLength: token.length,
      stack: error instanceof Error ? error.stack : undefined
    });
    return true;
  }
};

