use k256::ecdsa::{SigningKey as EcdsaSigningKey, signature::Signer, Signature as EcdsaSignature};
use k256::ecdsa::signature::hazmat::PrehashSigner;
use k256::schnorr::{SigningKey as SchnorrSigningKey, Signature as SchnorrSignature};

use sha2::{Sha256, Digest};
use sha3::Keccak256;
use thiserror::Error;
use hex;
use bech32::{Bech32, Hrp};

use crate::entropy::HardwareEntropy;
use crate::storage::{KeyStorage, FileSystemStorage};
use rand::{rngs::OsRng, RngCore};

#[derive(Error, Debug)]
pub enum IdentityError {
    #[error("Failed to generate hardware identity: {0}")]
    GenerationFailed(String),
    #[error("Signing failed: {0}")]
    SigningError(String),
}

/// All public-facing identity information derived from device hardware.
#[derive(Debug)]
pub struct IdentityInfo {
    /// Ethereum-compatible secp256k1 address (hex, 0x-prefixed)
    pub eth_address: String,
    /// Compressed public key (hex) for Ethereum use
    pub eth_pubkey_hex: String,
    /// Nostr public key — raw 32-byte x-coordinate as lowercase hex (NIP-01)
    pub nostr_pubkey_hex: String,
    /// Nostr public key in bech32 npub1… encoding (NIP-19)
    pub nostr_npub: String,
    /// Informational: which salt location is active
    pub salt_location: String,
    /// Optional camera ID embedded into hardware entropy
    pub camera_id: Option<String>,
}

/// The core device identity. Holds signing keys derived deterministically
/// from hardware entropy + persisted salt.
pub struct DeviceIdentity {
    pub info: IdentityInfo,
    // Private ECDSA key (k256) for Ethereum-style signing
    ecdsa_key: EcdsaSigningKey,
    // Schnorr signing key for Nostr — derived from the same seed bytes
    schnorr_key: SchnorrSigningKey,
}

impl DeviceIdentity {
    /// Create a new identity using the default FileSystemStorage.
    pub fn new(camera_id: Option<String>) -> Result<Self, IdentityError> {
        let storage = FileSystemStorage::new(None, None);
        Self::with_storage(camera_id, Box::new(storage))
    }

    /// Create a new identity with a specific storage backend.
    pub fn with_storage(camera_id: Option<String>, storage: Box<dyn KeyStorage>) -> Result<Self, IdentityError> {
        let entropy = HardwareEntropy::new(camera_id.clone())
            .map_err(|e| IdentityError::GenerationFailed(e.to_string()))?;

        // Get salt from storage or create a new one if it doesn't exist
        let salt = match storage.get_salt() {
            Ok(s) => {
                println!("✓ Salt loaded from {}", storage.get_location_info());
                s
            },
            Err(_) => {
                println!("Creating new device salt for {}...", storage.get_location_info());
                let mut new_salt = [0u8; 32];
                OsRng.fill_bytes(&mut new_salt);
                storage.store_salt(&new_salt)
                    .map_err(|e| IdentityError::GenerationFailed(format!("Failed to store salt: {}", e)))?;
                new_salt
            }
        };

        // --- Seed derivation ---
        // Matches the Python: hashlib.sha256(hw_id + salt).digest()
        let hw_id_bytes = entropy.get_hardware_id();
        let mut hasher = Sha256::new();
        hasher.update(&hw_id_bytes);
        hasher.update(&salt);
        let seed: [u8; 32] = hasher.finalize().into();

        // --- Ethereum ECDSA key (k256) ---
        let ecdsa_key = EcdsaSigningKey::from_slice(&seed)
            .map_err(|e| IdentityError::GenerationFailed(format!("Invalid ECDSA seed: {}", e)))?;
        let verifying_key = ecdsa_key.verifying_key();

        // Ethereum address: keccak256(uncompressed_pub_key[1..])[12..]
        let uncompressed = verifying_key.to_encoded_point(false);
        let pub_bytes = &uncompressed.as_bytes()[1..]; // strip 0x04 prefix
        let mut keccak = Keccak256::new();
        keccak.update(pub_bytes);
        let keccak_hash = keccak.finalize();
        let eth_address = format!("0x{}", hex::encode(&keccak_hash[12..]));
        let eth_pubkey_hex = hex::encode(verifying_key.to_sec1_bytes());

        // k256's SchnorrSigningKey is constructed from the same raw seed bytes, and its
        // verifying key exposes exactly the 32-byte x-coordinate used by Nostr.
        let schnorr_key = SchnorrSigningKey::from_bytes(&seed)
            .map_err(|e| IdentityError::GenerationFailed(format!("Invalid Schnorr seed: {}", e)))?;
        let nostr_verifying_key = schnorr_key.verifying_key();
        let nostr_pubkey_bytes: [u8; 32] = nostr_verifying_key.to_bytes().into();
        let nostr_pubkey_hex = hex::encode(nostr_pubkey_bytes);

        // Encode as bech32 npub1… (NIP-19)
        let hrp = Hrp::parse("npub").expect("valid HRP");
        let nostr_npub = bech32::encode::<Bech32>(hrp, &nostr_pubkey_bytes)
            .map_err(|e| IdentityError::GenerationFailed(format!("bech32 encode failed: {}", e)))?;

        Ok(Self {
            info: IdentityInfo {
                eth_address,
                eth_pubkey_hex,
                nostr_pubkey_hex,
                nostr_npub,
                salt_location: storage.get_location_info(),
                camera_id,
            },
            ecdsa_key,
            schnorr_key,
        })
    }

    // ── Ethereum / ECDSA ──────────────────────────────────────────────────────

    pub fn sign_data_ecdsa(&self, data: &[u8]) -> Result<Vec<u8>, IdentityError> {
        let sig: EcdsaSignature = self.ecdsa_key.sign(data);
        Ok(sig.normalize_s().unwrap_or(sig).to_bytes().to_vec())
    }

    pub fn sign_hash_ecdsa(&self, data_hash: &[u8; 32]) -> Result<String, IdentityError> {
        let sig: EcdsaSignature = self
            .ecdsa_key
            .sign_prehash(data_hash)
            .map_err(|e| IdentityError::SigningError(e.to_string()))?;
        Ok(hex::encode(sig.normalize_s().unwrap_or(sig).to_bytes()))
    }

    /// Sign a Nostr event ID (the 32-byte SHA-256 hash of the serialised event)
    pub fn sign_nostr_event(&self, event_id_hash: &[u8; 32]) -> Result<String, IdentityError> {
        let sig: SchnorrSignature = self
            .schnorr_key
            .sign_prehash(event_id_hash)
            .map_err(|e| IdentityError::SigningError(e.to_string()))?;
        Ok(hex::encode(sig.to_bytes()))
    }
}
