//! Hash finalization
//! Corresponds to hash_finalize @ 0x8000a792

use super::inner_compress::inner_compress;
use super::message::{process_message, u32x4_from_bytes};

/// Finalize: MD length padding + final compression + XOR truncation.
///
/// Parameters (from decompilation):
///   round_keys_and_state: 0x200 bytes (round keys + hash state)
///   chaining: 16 bytes chaining value
///   msg: message bytes
///
/// Returns: 16-byte digest
pub fn finalize(
    hash_state: &[u32; 8],
    chaining: &[u8; 16],
    msg: &[u8],
) -> [u8; 16] {
    let mut state = *hash_state;

    // 1. Append 0x01 marker
    process_message(&mut state, &[0x01]);

    // 2. Process remaining message
    process_message(&mut state, msg);

    // 3. Length encoding (5-byte little-endian bit length)
    let bit_len = msg.len() as u64;
    let byte_bits = bit_len.wrapping_mul(8); // convert to bits
    let mut len_block = [0u8; 16];
    len_block[0] = (byte_bits & 0xFF) as u8;
    len_block[1] = ((byte_bits >> 8) & 0xFF) as u8;
    len_block[2] = ((byte_bits >> 16) & 0xFF) as u8;
    len_block[3] = ((byte_bits >> 24) & 0xFF) as u8;
    len_block[4] = ((byte_bits >> 32) & 0xFF) as u8;

    // 4. Final compression
    let block = u32x4_from_bytes(&len_block);
    inner_compress(&mut state, &block);

    // 5. State copy (Feistel output arrangement)
    // copy state[4..5] -> state[0..1], state[6..7] -> state[2..3]
    state[0] = state[4];
    state[1] = state[5];
    state[2] = state[6];
    state[3] = state[7];

    // 6. XOR truncation: output[15-i] = state_bytes[i] ^ chaining[i]
    let state_bytes = state_to_bytes(&state);
    let mut output = [0u8; 16];
    for i in 0..16 {
        output[15 - i] = state_bytes[i] ^ chaining[i];
    }

    output
}

fn state_to_bytes(s: &[u32; 8]) -> [u8; 32] {
    let mut out = [0u8; 32];
    for i in 0..8 {
        out[i * 4..i * 4 + 4].copy_from_slice(&s[i].to_le_bytes());
    }
    out
}
