/**
 * Interview Token Validator
 *
 * Validation logic for interview scheduling tokens.
 * Validates token structure, expiry, usage status, and revocation.
 *
 * @module lib/validators/interview-token.validator
 */

import validator from 'validator';
import type {
  InterviewTokenPayload,
  InterviewTokenRecord,
  TokenValidationResult,
} from '@/lib/types/interview-token.types';

/**
 * Validate Token Configuration
 */
export interface ValidateTokenConfig {
  /** Token to validate */
  token: string;

  /** Decoded token payload (if JWT decode succeeded) */
  payload?: InterviewTokenPayload;

  /** Database token record (if found) */
  tokenRecord?: InterviewTokenRecord;
}

/**
 * Validate token structure and format
 *
 * @param token - JWT token string
 * @returns Whether token format is valid
 */
export function validateTokenFormat(token: string): boolean {
  if (!token || typeof token !== 'string') {
    return false;
  }

  // Basic JWT format check: three base64url parts separated by dots
  const parts = token.split('.');
  if (parts.length !== 3) {
    return false;
  }

  // Each part should be non-empty base64url
  return parts.every(part => part.length > 0 && /^[A-Za-z0-9_-]+$/.test(part));
}

/**
 * Validate token payload structure
 *
 * @param payload - Decoded JWT payload
 * @returns Validation result with error message if invalid
 */
export function validateTokenPayload(
  payload: any
): { valid: boolean; error?: string } {
  if (!payload || typeof payload !== 'object') {
    return { valid: false, error: 'Invalid token payload structure' };
  }

  // Check required fields
  if (!payload.applicationId || typeof payload.applicationId !== 'string') {
    return { valid: false, error: 'Missing or invalid applicationId' };
  }

  if (!payload.jobId || typeof payload.jobId !== 'string') {
    return { valid: false, error: 'Missing or invalid jobId' };
  }

  if (!payload.applicantEmail || typeof payload.applicantEmail !== 'string') {
    return { valid: false, error: 'Missing or invalid applicantEmail' };
  }

  // Validate email format
  if (!validator.isEmail(payload.applicantEmail)) {
    return { valid: false, error: 'Invalid email format in token' };
  }

  // Check JWT standard fields
  if (!payload.exp || typeof payload.exp !== 'number') {
    return { valid: false, error: 'Missing or invalid expiration time' };
  }

  if (!payload.iat || typeof payload.iat !== 'number') {
    return { valid: false, error: 'Missing or invalid issued time' };
  }

  if (!payload.jti || typeof payload.jti !== 'string') {
    return { valid: false, error: 'Missing or invalid token ID (jti)' };
  }

  // Validate UUIDs format
  const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

  if (!uuidRegex.test(payload.applicationId)) {
    return { valid: false, error: 'Invalid UUID format for applicationId' };
  }

  if (!uuidRegex.test(payload.jobId)) {
    return { valid: false, error: 'Invalid UUID format for jobId' };
  }

  if (!uuidRegex.test(payload.jti)) {
    return { valid: false, error: 'Invalid UUID format for token ID' };
  }

  return { valid: true };
}

/**
 * Validate token expiration
 *
 * @param payload - Decoded JWT payload
 * @returns Validation result with error message if expired
 */
export function validateTokenExpiry(
  payload: InterviewTokenPayload
): { valid: boolean; error?: string; errorCode?: string } {
  const now = Math.floor(Date.now() / 1000); // Current time in seconds

  if (payload.exp < now) {
    const expiredDate = new Date(payload.exp * 1000);
    return {
      valid: false,
      error: `Token expired on ${expiredDate.toISOString()}`,
      errorCode: 'EXPIRED',
    };
  }

  return { valid: true };
}

/**
 * Validate token usage status
 *
 * @param tokenRecord - Database token record
 * @returns Validation result with error message if already used
 */
export function validateTokenUsage(
  tokenRecord: InterviewTokenRecord
): { valid: boolean; error?: string; errorCode?: string } {
  if (tokenRecord.usedAt !== null) {
    return {
      valid: false,
      error: `Token already used on ${tokenRecord.usedAt.toISOString()}`,
      errorCode: 'USED',
    };
  }

  return { valid: true };
}

/**
 * Validate token revocation status
 *
 * @param tokenRecord - Database token record
 * @returns Validation result with error message if revoked
 */
export function validateTokenRevocation(
  tokenRecord: InterviewTokenRecord
): { valid: boolean; error?: string; errorCode?: string } {
  if (tokenRecord.isRevoked) {
    return {
      valid: false,
      error: 'Token has been revoked',
      errorCode: 'REVOKED',
    };
  }

  return { valid: true };
}

/**
 * Validate email matches application
 *
 * @param tokenEmail - Email from token
 * @param applicationEmail - Email from application record
 * @returns Validation result with error message if mismatch
 */
export function validateEmailMatch(
  tokenEmail: string,
  applicationEmail: string
): { valid: boolean; error?: string } {
  if (tokenEmail.toLowerCase() !== applicationEmail.toLowerCase()) {
    return {
      valid: false,
      error: 'Token email does not match application email',
    };
  }

  return { valid: true };
}

/**
 * Comprehensive token validation
 *
 * Performs all validation checks in sequence.
 *
 * @param config - Validation configuration
 * @returns Complete validation result
 */
export function validateToken(config: ValidateTokenConfig): TokenValidationResult {
  // Check token format
  if (!validateTokenFormat(config.token)) {
    return {
      valid: false,
      error: 'Invalid token format',
      errorCode: 'INVALID',
    };
  }

  // Check payload structure (if decoded)
  if (config.payload) {
    const payloadValidation = validateTokenPayload(config.payload);
    if (!payloadValidation.valid) {
      return {
        valid: false,
        error: payloadValidation.error,
        errorCode: 'INVALID',
      };
    }

    // Check expiry
    const expiryValidation = validateTokenExpiry(config.payload);
    if (!expiryValidation.valid) {
      return {
        valid: false,
        error: expiryValidation.error,
        errorCode: expiryValidation.errorCode as any,
      };
    }
  }

  // Check database record (if found)
  if (config.tokenRecord) {
    // Check usage
    const usageValidation = validateTokenUsage(config.tokenRecord);
    if (!usageValidation.valid) {
      return {
        valid: false,
        error: usageValidation.error,
        errorCode: usageValidation.errorCode as any,
        tokenRecord: config.tokenRecord,
      };
    }

    // Check revocation
    const revocationValidation = validateTokenRevocation(config.tokenRecord);
    if (!revocationValidation.valid) {
      return {
        valid: false,
        error: revocationValidation.error,
        errorCode: revocationValidation.errorCode as any,
        tokenRecord: config.tokenRecord,
      };
    }
  }

  return {
    valid: true,
    payload: config.payload,
    tokenRecord: config.tokenRecord,
  };
}

/**
 * Validate token generation rate limit
 *
 * @param recentTokenCount - Number of tokens generated recently
 * @param rateLimit - Maximum allowed tokens per period
 * @returns Validation result with error message if rate limited
 */
export function validateRateLimit(
  recentTokenCount: number,
  rateLimit: number
): { valid: boolean; error?: string } {
  if (recentTokenCount >= rateLimit) {
    return {
      valid: false,
      error: `Rate limit exceeded: maximum ${rateLimit} tokens per hour`,
    };
  }

  return { valid: true };
}
