use ed25519_dalek::{Signer as Ed25519Signer, SigningKey};
use soroban_sdk::{contract, Bytes, BytesN, Env};

use crate::verifiers::ed25519::{batch_canonicalize_key, verify};

#[contract]
struct MockContract;

#[test]
fn ed25519_verify_success() {
    let e = Env::default();
    let address = e.register(MockContract, ());

    let secret_key: [u8; 32] = [
        157, 97, 177, 157, 239, 253, 90, 96, 186, 132, 74, 244, 146, 236, 44, 196, 68, 73, 197,
        105, 123, 50, 105, 25, 112, 59, 172, 3, 28, 174, 127, 96,
    ];

    let signing_key = SigningKey::from_bytes(&secret_key);
    let verifying_key = signing_key.verifying_key();

    let key_data = BytesN::<32>::from_array(&e, verifying_key.as_bytes());

    let data = Bytes::from_array(&e, &[1u8; 64]);
    let signature_payload = e.crypto().keccak256(&data);

    let signature = signing_key.sign(&signature_payload.to_array()).to_bytes();
    let sig_data = BytesN::<64>::from_array(&e, &signature);

    e.as_contract(&address, || {
        assert!(verify(
            &e,
            &Bytes::from_array(&e, &signature_payload.to_array()),
            &key_data,
            &sig_data
        ))
    });
}

#[test]
#[should_panic(expected = "Error(Crypto, InvalidInput)")]
fn ed25519_verify_invalid_signature() {
    let e = Env::default();
    let address = e.register(MockContract, ());

    e.as_contract(&address, || {
        let secret_key: [u8; 32] = [
            157, 97, 177, 157, 239, 253, 90, 96, 186, 132, 74, 244, 146, 236, 44, 196, 68, 73, 197,
            105, 123, 50, 105, 25, 112, 59, 172, 3, 28, 174, 127, 96,
        ];

        let signing_key = SigningKey::from_bytes(&secret_key);
        let verifying_key = signing_key.verifying_key();
        let key_data = BytesN::<32>::from_array(&e, verifying_key.as_bytes());

        let data = Bytes::from_array(&e, &[1u8; 64]);
        let signature_payload = e.crypto().keccak256(&data);

        // Create a valid signature but modify it to make it invalid
        let mut signature = signing_key.sign(&signature_payload.to_array()).to_bytes();
        signature[0] = signature[0].wrapping_add(1); // Corrupt the signature

        let sig_data = BytesN::<64>::from_array(&e, &signature);

        verify(&e, &Bytes::from_array(&e, &signature_payload.to_array()), &key_data, &sig_data);
    });
}

#[test]
#[should_panic(expected = "Error(Crypto, InvalidInput)")]
fn ed25519_verify_wrong_key() {
    let e = Env::default();
    let address = e.register(MockContract, ());

    e.as_contract(&address, || {
        let secret_key1: [u8; 32] = [
            157, 97, 177, 157, 239, 253, 90, 96, 186, 132, 74, 244, 146, 236, 44, 196, 68, 73, 197,
            105, 123, 50, 105, 25, 112, 59, 172, 3, 28, 174, 127, 96,
        ];
        let secret_key2: [u8; 32] = [
            200, 100, 150, 200, 240, 250, 95, 100, 190, 140, 80, 250, 150, 240, 50, 200, 70, 80,
            200, 110, 130, 55, 110, 30, 115, 65, 175, 10, 35, 180, 130, 100,
        ];

        let signing_key1 = SigningKey::from_bytes(&secret_key1);
        let signing_key2 = SigningKey::from_bytes(&secret_key2);

        // Use key1's public key but key2's signature
        let verifying_key1 = signing_key1.verifying_key();
        let key_data = BytesN::<32>::from_array(&e, verifying_key1.as_bytes());

        let data = Bytes::from_array(&e, &[1u8; 64]);
        let signature_payload = e.crypto().keccak256(&data);

        // Sign with key2 but verify with key1
        let signature = signing_key2.sign(&signature_payload.to_array()).to_bytes();
        let sig_data = BytesN::<64>::from_array(&e, &signature);

        verify(&e, &Bytes::from_array(&e, &signature_payload.to_array()), &key_data, &sig_data);
    });
}

#[test]
fn ed25519_canonicalize_key() {
    let e = Env::default();
    let address = e.register(MockContract, ());

    e.as_contract(&address, || {
        let key_bytes = [42u8; 32];
        let public_key = BytesN::<32>::from_array(&e, &key_bytes);

        let canonical = crate::verifiers::ed25519::canonicalize_key(&e, &public_key);

        assert_eq!(canonical, Bytes::from_array(&e, &key_bytes));
        assert_eq!(canonical.len(), 32);
    });
}

#[test]
fn ed25519_batch_canonicalize_key_preserves_order() {
    let e = Env::default();
    let address = e.register(MockContract, ());

    e.as_contract(&address, || {
        let key1 = BytesN::<32>::from_array(&e, &[1u8; 32]);
        let key2 = BytesN::<32>::from_array(&e, &[2u8; 32]);
        let key3 = BytesN::<32>::from_array(&e, &[3u8; 32]);

        let keys = soroban_sdk::Vec::from_array(&e, [key1, key2, key3]);
        let canonical = batch_canonicalize_key(&e, &keys);

        assert_eq!(canonical.len(), 3);
        assert_eq!(canonical.get(0).unwrap(), Bytes::from_array(&e, &[1u8; 32]));
        assert_eq!(canonical.get(1).unwrap(), Bytes::from_array(&e, &[2u8; 32]));
        assert_eq!(canonical.get(2).unwrap(), Bytes::from_array(&e, &[3u8; 32]));
    });
}
