package org.ds;

import java.security.MessageDigest;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * Implementation of the "Multiple Hash Functions" solution from the slide.
 * Maps each node to the ring using distinct mathematical algorithms.
 */
public class MultiAlgorithmConsistentHash<T> extends ConsistentHash<T> {

    private final List<String> algorithms = Arrays.asList("MD5", "SHA-1", "SHA-256");

    public MultiAlgorithmConsistentHash(int virtualNodes) {
        super(virtualNodes);
    }

    @Override
    public void addNode(T node) {
        // As per the slide: Apply multiple hash functions on each node
        for (int i = 0; i < virtualNodes; i++) {
            // Cycle through algorithms (MD5, SHA1, SHA256) for each virtual node
            String algo = algorithms.get(i % algorithms.size());
            long hash = calculateHash(node.toString() + ":" + i, algo);
            
            // Put directly into the protected ring
            ring.put(hash, node);
        }
    }

    @Override
    public void removeNode(T node) {
        for (int i = 0; i < virtualNodes; i++) {
            String algo = algorithms.get(i % algorithms.size());
            long hash = calculateHash(node.toString() + ":" + i, algo);
            ring.remove(hash);
        }
    }

    private long calculateHash(String key, String algorithm) {
        try {
            MessageDigest md = MessageDigest.getInstance(algorithm);
            byte[] bytes = md.digest(key.getBytes(StandardCharsets.UTF_8));
            long hash = 0;
            // Use 8 bytes for a 64-bit long hash
            for (int i = 0; i < Math.min(bytes.length, 8); i++) {
                hash = (hash << 8) | (bytes[i] & 0xFF);
            }
            return Math.abs(hash);
        } catch (Exception e) {
            throw new RuntimeException("Error calculating " + algorithm, e);
        }
    }
}