//! KDBX 3.1 complete read pipeline
//!
//! Pipeline: signature → outer header → key derivation → decrypt → verify streamStartBytes
//!           → hashed block stream → decompress → inner stream decrypt → XML → Database

use std::io::Read;


use crate::crypto::compression::CompressionAlgorithm;
use crate::crypto::inner_stream::create_inner_stream;
use crate::model::db::composite_key::CompositeKey;
use crate::model::db::database::{Database, DatabaseVersion};
use crate::model::exception::{DatabaseError, DatabaseResult};
use crate::kdbx::file::header::KdbxHeader31;
use crate::kdbx::file::reader::DatabaseReader;
use crate::kdbx::kdf::aes_kdf::{AesKdf, AES_KDF_UUID};
use crate::kdbx::kdf::kdf_parameters::KdfParameters;
use crate::kdbx::kdf::kdf_engine::KdfEngine;
use crate::kdbx::stream::hashed_block::HashedBlockReader;
use crate::kdbx::xml::KdbxXmlReader;

/// Read a KDBX 3.1 database from a reader.
pub fn read_kdbx31<R: Read>(
    reader: &mut R,
    composite_key: &CompositeKey,
) -> DatabaseResult<Database> {
    // 1. Read and verify signature
    let version = DatabaseReader::detect_version(reader)?;
    if version != DatabaseVersion::KDBX31 {
        return Err(DatabaseError::InvalidVersion(
            format!("Expected KDBX 3.1, got {version:?}"),
        ));
    }

    // 2. Read outer header
    let header = DatabaseReader::read_kdbx31_header(reader)?;

    // 3. Derive final key
    let final_key = derive_kdbx31_key(composite_key, &header)?;

    // 4. Read encrypted payload
    let mut encrypted = Vec::new();
    reader.read_to_end(&mut encrypted)?;

    // 5. Decrypt
    let cipher = crate::crypto::cipher_engine::create_cipher_engine(header.encryption_algorithm);
    let decrypted = cipher.decrypt(&final_key, &header.encryption_iv, &encrypted)
        .map_err(|e| DatabaseError::DecryptionError(e.to_string()))?;

    // 6. Verify stream start bytes
    if decrypted.len() < 32 {
        return Err(DatabaseError::DecryptionError("Decrypted data too short".into()));
    }
    let expected = crate::crypto::HashEngine::sha256(&final_key);
    if decrypted[..32] != expected {
        return Err(DatabaseError::InvalidKey);
    }

    // 7. Read hashed blocks → compressed XML
    let block_data = &decrypted[32..];
    let mut block_reader = HashedBlockReader::new(std::io::Cursor::new(block_data));
    let compressed = block_reader.read_all()?;

    // 8. Decompress
    let xml_data = match header.compression {
        CompressionAlgorithm::Gzip => crate::crypto::compression::decompress(&compressed)?,
        CompressionAlgorithm::None => compressed,
    };

    // 9. Parse XML with inner stream protection
    let xml_str = String::from_utf8(xml_data)
        .map_err(|e| DatabaseError::InvalidFormat(format!("XML not UTF-8: {e}")))?;

    let mut inner_stream = create_inner_stream(header.inner_random_stream, &header.inner_random_stream_key)?;
    let mut database = KdbxXmlReader::read(&xml_str, inner_stream.as_mut())?;

    // 10. Populate database metadata from header
    database.version = DatabaseVersion::KDBX31;
    database.encryption_algorithm = header.encryption_algorithm;
    database.compression = header.compression;
    database.loaded = true;

    Ok(database)
}

/// Derive the final encryption key for KDBX 3.1 using AES-KDF.
fn derive_kdbx31_key(composite_key: &CompositeKey, header: &KdbxHeader31) -> DatabaseResult<Vec<u8>> {
    let raw_key = composite_key.build_raw_key();
    if raw_key.is_empty() {
        return Err(DatabaseError::InvalidKey);
    }

    // Build KDF parameters from header
    let mut params = KdfParameters::new(AES_KDF_UUID);
    params.set_byte_array("S", &header.transform_seed);
    params.set_uint64("R", header.transform_rounds);

    // Transform key with AES-KDF
    let kdf = AesKdf;
    let transformed = kdf.transform(&raw_key, &params)?;

    // Final key = SHA-256(masterSeed || transformedKey)
    let mut combined = Vec::with_capacity(header.master_seed.len() + transformed.len());
    combined.extend_from_slice(&header.master_seed);
    combined.extend_from_slice(&transformed);
    Ok(crate::crypto::HashEngine::sha256(&combined).to_vec())
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::model::db::composite_key::CompositeKey;

    #[test]
    fn test_invalid_signature_rejected() {
        let mut data = Vec::new();
        data.extend_from_slice(&0xDEADBEEFu32.to_le_bytes());
        data.extend_from_slice(&0xDEADBEEFu32.to_le_bytes());
        data.extend_from_slice(&0x00030001u32.to_le_bytes());

        let key = CompositeKey::new().with_password(b"test");
        let mut cursor = std::io::Cursor::new(data);
        let result = read_kdbx31(&mut cursor, &key);
        assert!(result.is_err());
    }
}
