use crate::seed::Seed;
use ed25519_dalek::{Signature, Signer, SigningKey, Verifier, VerifyingKey};
use std::io::Read;
use std::path::Path;

pub fn derive_public(seed: &Seed, realm: &str) -> String {
    let raw = seed.derive_32(realm, "sign");
    let signing_key = SigningKey::from_bytes(&raw);
    let verifying_key = signing_key.verifying_key();

    use base64::Engine;
    base64::engine::general_purpose::STANDARD.encode(verifying_key.as_bytes())
}

pub fn sign(
    seed: &Seed,
    realm: &str,
    input: Option<&Path>,
) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
    let raw = seed.derive_32(realm, "sign");
    let signing_key = SigningKey::from_bytes(&raw);

    let data = read_input(input)?;
    let signature = signing_key.sign(&data);

    Ok(signature.to_bytes().to_vec())
}

pub fn verify(
    pubkey_b64: &str,
    signature: &[u8],
    input: Option<&Path>,
) -> Result<bool, Box<dyn std::error::Error>> {
    use base64::Engine;

    let pubkey_bytes = base64::engine::general_purpose::STANDARD.decode(pubkey_b64)?;
    let pubkey_array: [u8; 32] = pubkey_bytes
        .try_into()
        .map_err(|_| "invalid public key length")?;

    let sig_array: [u8; 64] = signature
        .try_into()
        .map_err(|_| "invalid signature length")?;

    let verifying_key = VerifyingKey::from_bytes(&pubkey_array)?;
    let signature = Signature::from_bytes(&sig_array);

    let data = read_input(input)?;

    Ok(verifying_key.verify(&data, &signature).is_ok())
}

fn read_input(path: Option<&Path>) -> Result<Vec<u8>, Box<dyn std::error::Error>> {
    match path {
        Some(p) => Ok(std::fs::read(p)?),
        None => {
            let mut buf = vec![];
            std::io::stdin().read_to_end(&mut buf)?;
            Ok(buf)
        }
    }
}
