const crypto = require('crypto');
const axios = require('axios');

class HaveIBeenPwnedAPI {
    constructor(config = {}) {
        this.baseUrl = 'https://api.pwnedpasswords.com';
        this.userAgent = config.userAgent || 'PasswordSecurityChecker';
    }

    /**
     * Generates a SHA-1 hash of the password
     * @param {string} password - The password to hash
     * @returns {string} The uppercase SHA-1 hash
     */
    generateHash(password) {
        return crypto
            .createHash('sha1')
            .update(password)
            .digest('hex')
            .toUpperCase();
    }

    /**
     * Checks if a password has been exposed in known data breaches
     * @param {string} password - The password to check
     * @returns {Promise<{isCompromised: boolean, timesExposed: number}>}
     */
    async checkPassword(password) {
        try {
            // Generate hash and get first 5 characters for k-anonymity
            const hash = this.generateHash(password);
            const hashPrefix = hash.substring(0, 5);
            const hashSuffix = hash.substring(5);

            // Make request to HIBP API
            const response = await axios.get(`${this.baseUrl}/range/${hashPrefix}`, {
                headers: {
                    'User-Agent': this.userAgent
                }
            });

            // Parse response and check if password hash suffix exists
            const hashes = response.data.split('\n');
            const match = hashes.find(h => h.split(':')[0] === hashSuffix);

            if (match) {
                const timesExposed = parseInt(match.split(':')[1]);
                return {
                    isCompromised: true,
                    timesExposed
                };
            }

            return {
                isCompromised: false,
                timesExposed: 0
            };

        } catch (error) {
            throw new Error(`Failed to check password: ${error.message}`);
        }
    }

    /**
     * Validates a password against HIBP and custom rules
     * @param {string} password - The password to validate
     * @param {Object} options - Validation options
     * @param {number} options.maxExposures - Maximum allowed exposures (default: 0)
     * @returns {Promise<{isValid: boolean, reason?: string}>}
     */
    async validatePassword(password, options = { maxExposures: 0 }) {
        try {
            const result = await this.checkPassword(password);
            
            if (result.timesExposed > options.maxExposures) {
                return {
                    isValid: false,
                    reason: `Password has been exposed ${result.timesExposed} times in data breaches`
                };
            }

            return {
                isValid: true
            };

        } catch (error) {
            throw new Error(`Password validation failed: ${error.message}`);
        }
    }
}

module.exports = HaveIBeenPwnedAPI;
