//! Top-level PoW solver
//! Corresponds to pow_main_dispatch (Yb) solve path (0xABAB270C)
//!
//! The `n` value is NOT a single nonce — it's an accumulated Vec<u8> of
//! 16-byte hash digests, one per iteration. The full output is base64-encoded
//! and typically 20-40KB (1250-2500 iterations × 16 bytes).

use crate::pcg::PcgRng;
use crate::sbox::apply_polynomial_sbox;
use crate::gift256;
use crate::hash;

/// Challenge data parsed from JWT `d` field
pub struct Challenge {
    pub key_material: [u8; 32],  // 32-byte key material (before S-Box)
    pub difficulty: u32,          // iteration count (c field from JWT)
}

/// Solve the PoW challenge by accumulating digests.
///
/// Returns a Vec<u8> of concatenated 16-byte digests (one per iteration).
/// This is base64-encoded to produce the `n` value sent back to hCaptcha.
///
/// Full per-iteration flow (0xABAB270C path):
///   1. PCG generates 12-byte nonce
///   2. S-Box polynomial substitution on key material
///   3. GIFT-256 key schedule → 120 u32 round keys
///   4. MMO compression → chaining value
///   5. GIFT-256 encrypt (nonce as plaintext input)
///   6. hash_finalize → 16-byte digest
///   7. Append digest to output accumulator
///   8. Loop back to step 1 with next PCG nonce
pub fn solve(challenge: &Challenge, seed: u64) -> Vec<u8> {
    let mut rng = PcgRng::new(seed);
    let iterations = challenge.difficulty;
    let mut output = Vec::with_capacity(iterations as usize * 16);

    // Pre-compute key schedule and MMO (invariant across iterations)
    let mut key_data = challenge.key_material;
    apply_polynomial_sbox(&mut key_data);

    let round_keys = gift256::key_schedule::key_schedule(&key_data);
    let mmo_state = hash::mmo::mmo_compress(&round_keys);

    for _iter in 0..iterations {
        // 1. Generate 12-byte nonce
        let nonce = rng.generate_nonce();

        // 2. Assemble hash input block from nonce
        let nonce_u32_0 = u32::from_le_bytes([nonce[0], nonce[1], nonce[2], nonce[3]]);
        let nonce_u32_1 = u32::from_le_bytes([nonce[4], nonce[5], nonce[6], nonce[7]]);
        let nonce_u32_2 = u32::from_le_bytes([nonce[8], nonce[9], nonce[10], nonce[11]]);

        // 3. GIFT-256 encrypt (nonce words + padding as plaintext)
        let encrypted = gift256::encrypt::encrypt(
            &[nonce_u32_0, nonce_u32_1, nonce_u32_2, 0x01000000, 0, 0, 0, 0],
            &round_keys,
        );

        // 4. Build hash state from encrypted output
        let hash_state = encrypted;

        // 5. Finalize hash → 16-byte digest
        let digest = hash::finalize::finalize(
            &hash_state,
            &mmo_state.chaining,
            &nonce,
        );

        // 6. Append 16-byte digest to output accumulator
        output.extend_from_slice(&digest);
    }

    output
}
