use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit};
use base64::{engine::general_purpose::STANDARD as BASE64, Engine};
use hex;
use pbkdf2::pbkdf2_hmac;
use rand::RngCore;
use sha2::{Digest, Sha256, Sha512};

use crate::error::{AppError, AppResult};

const KEY_LENGTH: usize = 32; // 256 bits
const IV_LENGTH: usize = 16; // 128 bits
const SALT_LENGTH: usize = 64;
const PBKDF2_ITERATIONS: u32 = 100_000;

type Aes256CbcEnc = cbc::Encryptor<aes::Aes256>;
type Aes256CbcDec = cbc::Decryptor<aes::Aes256>;

/// Generate a unique encryption key for a user.
/// Key is generated once at first login and stored securely in keyring.
/// Returns Base64 encoded salt+key combination.
pub fn generate_user_encryption_key(user_id: &str) -> AppResult<String> {
    let mut salt = vec![0u8; SALT_LENGTH];
    rand::rng().fill_bytes(&mut salt);

    let mut key = vec![0u8; KEY_LENGTH];
    pbkdf2_hmac::<Sha512>(user_id.as_bytes(), &salt, PBKDF2_ITERATIONS, &mut key);

    let mut combined = Vec::with_capacity(SALT_LENGTH + KEY_LENGTH);
    combined.extend_from_slice(&salt);
    combined.extend_from_slice(&key);

    Ok(BASE64.encode(&combined))
}

/// Extract the actual encryption key from the stored combined salt+key.
fn extract_key_from_stored(stored_key: &str) -> AppResult<Vec<u8>> {
    let combined = BASE64
        .decode(stored_key)
        .map_err(|e| AppError::Encryption(format!("Invalid base64 key: {}", e)))?;

    if combined.len() < SALT_LENGTH + KEY_LENGTH {
        return Err(AppError::Encryption("Stored key too short".to_string()));
    }

    Ok(combined[SALT_LENGTH..SALT_LENGTH + KEY_LENGTH].to_vec())
}

/// Encrypt data with user key (AES-256-CBC).
/// Returns encrypted string with format "iv_hex:encrypted_hex".
/// Compatible with the Fastify/Electron TypeScript implementation.
pub fn encrypt_data_with_user_key(data: &str, user_key: &str) -> AppResult<String> {
    let key = extract_key_from_stored(user_key)?;

    let mut iv = [0u8; IV_LENGTH];
    rand::rng().fill_bytes(&mut iv);

    let data_bytes = data.as_bytes();
    // PKCS7 padding
    let block_size = 16;
    let padding_len = block_size - (data_bytes.len() % block_size);
    let mut buf = Vec::with_capacity(data_bytes.len() + padding_len);
    buf.extend_from_slice(data_bytes);
    buf.extend(std::iter::repeat(padding_len as u8).take(padding_len));

    let key_array: [u8; 32] = key
        .as_slice()
        .try_into()
        .map_err(|_| AppError::Encryption("Invalid key length".to_string()))?;

    let cipher = Aes256CbcEnc::new(&key_array.into(), &iv.into());
    let padded_length = buf.len();
    cipher
        .encrypt_padded_mut::<aes::cipher::block_padding::NoPadding>(&mut buf, padded_length)
        .map_err(|encryption_error| AppError::Encryption(format!("Encryption failed: {}", encryption_error)))?;

    let iv_hex = hex::encode(iv);
    let encrypted_hex = hex::encode(&buf);

    Ok(format!("{}:{}", iv_hex, encrypted_hex))
}

/// Decrypt data with user key (AES-256-CBC).
/// Expects encrypted string with format "iv_hex:encrypted_hex".
/// Compatible with the Fastify/Electron TypeScript implementation.
pub fn decrypt_data_with_user_key(encrypted_data: &str, user_key: &str) -> AppResult<String> {
    let parts: Vec<&str> = encrypted_data.splitn(2, ':').collect();
    if parts.len() != 2 {
        return Err(AppError::Encryption(
            "Invalid encrypted data format, expected 'iv:data'".to_string(),
        ));
    }

    let iv = hex::decode(parts[0])
        .map_err(|e| AppError::Encryption(format!("Invalid IV hex: {}", e)))?;
    let encrypted_bytes = hex::decode(parts[1])
        .map_err(|e| AppError::Encryption(format!("Invalid encrypted hex: {}", e)))?;

    let key = extract_key_from_stored(user_key)?;

    let key_array: [u8; 32] = key
        .as_slice()
        .try_into()
        .map_err(|_| AppError::Encryption("Invalid key length".to_string()))?;
    let iv_array: [u8; 16] = iv
        .as_slice()
        .try_into()
        .map_err(|_| AppError::Encryption("Invalid IV length".to_string()))?;

    let mut buf = encrypted_bytes.clone();
    let cipher = Aes256CbcDec::new(&key_array.into(), &iv_array.into());

    let decrypted = cipher
        .decrypt_padded_mut::<aes::cipher::block_padding::Pkcs7>(&mut buf)
        .map_err(|e| AppError::Encryption(format!("Decryption failed: {}", e)))?;

    String::from_utf8(decrypted.to_vec())
        .map_err(|e| AppError::Encryption(format!("Invalid UTF-8 after decryption: {}", e)))
}

/// Hash data using SHA-256 (for non-reversible hashing like titles).
/// Returns hex encoded hash.
pub fn hash_element(data: &str) -> String {
    let normalized = data.to_lowercase().trim().to_string();
    let mut hasher = Sha256::new();
    hasher.update(normalized.as_bytes());
    hex::encode(hasher.finalize())
}
