use capnp::capability::Promise;
use quicproquo_proto::node_capnp::node_service;

use crate::auth::{coded_error, fmt_hex, require_identity_or_request, validate_auth_context};
use crate::error_codes::*;
use crate::metrics;
use crate::storage::StorageError;

use super::NodeServiceImpl;

fn storage_err(err: StorageError) -> capnp::Error {
    coded_error(E009_STORAGE_ERROR, err)
}

const MAX_KEYPACKAGE_BYTES: usize = 1024 * 1024; // 1 MB cap per KeyPackage

impl NodeServiceImpl {
    pub fn handle_upload_key_package(
        &mut self,
        params: node_service::UploadKeyPackageParams,
        mut results: node_service::UploadKeyPackageResults,
    ) -> Promise<(), capnp::Error> {
        let (auth_ctx, identity_key, package) = match params.get() {
            Ok(p) => {
                let auth_ctx = match validate_auth_context(&self.auth_cfg, &self.sessions, p.get_auth()) {
                    Ok(ctx) => ctx,
                    Err(e) => return Promise::err(e),
                };
                let ik = match p.get_identity_key() {
                    Ok(v) => v.to_vec(),
                    Err(e) => return Promise::err(coded_error(E020_BAD_PARAMS, e)),
                };
                let pkg = match p.get_package() {
                    Ok(v) => v.to_vec(),
                    Err(e) => return Promise::err(coded_error(E020_BAD_PARAMS, e)),
                };
                (auth_ctx, ik, pkg)
            }
            Err(e) => return Promise::err(e),
        };

        if identity_key.len() != 32 {
            return Promise::err(coded_error(
                E004_IDENTITY_KEY_LENGTH,
                format!("identityKey must be exactly 32 bytes, got {}", identity_key.len()),
            ));
        }
        if package.is_empty() {
            return Promise::err(coded_error(E007_PACKAGE_EMPTY, "package must not be empty"));
        }
        if package.len() > MAX_KEYPACKAGE_BYTES {
            return Promise::err(coded_error(
                E008_PACKAGE_TOO_LARGE,
                format!("package exceeds max size ({} bytes)", MAX_KEYPACKAGE_BYTES),
            ));
        }

        if let Err(e) = require_identity_or_request(
            &auth_ctx,
            &identity_key,
            self.auth_cfg.allow_insecure_identity_from_request,
        ) {
            return Promise::err(e);
        }

        if let Err(e) = quicproquo_core::validate_keypackage_ciphersuite(&package) {
            return Promise::err(coded_error(
                E021_CIPHERSUITE_NOT_ALLOWED,
                format!("KeyPackage ciphersuite not allowed: {e}"),
            ));
        }

        let fingerprint: Vec<u8> = crate::auth::fingerprint(&package);
        if let Err(e) = self
            .store
            .upload_key_package(&identity_key, package)
            .map_err(storage_err)
        {
            return Promise::err(e);
        }

        results.get().set_fingerprint(&fingerprint);

        metrics::record_key_package_upload_total();
        // Audit: KeyPackage upload — only fingerprint prefix, no secrets.
        tracing::info!(
            identity_prefix = %fmt_hex(&identity_key[..4]),
            fingerprint_prefix = %fmt_hex(&fingerprint[..4]),
            "audit: key_package_upload"
        );

        Promise::ok(())
    }

    pub fn handle_fetch_key_package(
        &mut self,
        params: node_service::FetchKeyPackageParams,
        mut results: node_service::FetchKeyPackageResults,
    ) -> Promise<(), capnp::Error> {
        let identity_key = match params.get() {
            Ok(p) => match p.get_identity_key() {
                Ok(v) => v.to_vec(),
                Err(e) => return Promise::err(coded_error(E020_BAD_PARAMS, e)),
            },
            Err(e) => return Promise::err(coded_error(E020_BAD_PARAMS, e)),
        };
        if let Err(e) = params
            .get()
            .ok()
            .map(|p| crate::auth::validate_auth(&self.auth_cfg, &self.sessions, p.get_auth()))
            .transpose()
        {
            return Promise::err(e);
        }

        if identity_key.len() != 32 {
            return Promise::err(coded_error(
                E004_IDENTITY_KEY_LENGTH,
                format!("identityKey must be exactly 32 bytes, got {}", identity_key.len()),
            ));
        }

        let package = match self
            .store
            .fetch_key_package(&identity_key)
            .map_err(storage_err)
        {
            Ok(p) => p,
            Err(e) => return Promise::err(e),
        };

        match package {
            Some(pkg) => {
                tracing::debug!(identity = %fmt_hex(&identity_key[..4]), "KeyPackage fetched");
                results.get().set_package(&pkg);
            }
            None => {
                tracing::debug!(
                    identity = %fmt_hex(&identity_key[..4]),
                    "no KeyPackage available for identity"
                );
                results.get().set_package(&[]);
            }
        }

        Promise::ok(())
    }

    pub fn handle_upload_hybrid_key(
        &mut self,
        params: node_service::UploadHybridKeyParams,
        _results: node_service::UploadHybridKeyResults,
    ) -> Promise<(), capnp::Error> {
        let p = match params.get() {
            Ok(p) => p,
            Err(e) => return Promise::err(coded_error(E020_BAD_PARAMS, e)),
        };
        let identity_key = match p.get_identity_key() {
            Ok(v) => v.to_vec(),
            Err(e) => return Promise::err(coded_error(E020_BAD_PARAMS, e)),
        };
        let hybrid_pk = match p.get_hybrid_public_key() {
            Ok(v) => v.to_vec(),
            Err(e) => return Promise::err(coded_error(E020_BAD_PARAMS, e)),
        };

        let auth_ctx = match validate_auth_context(&self.auth_cfg, &self.sessions, p.get_auth()) {
            Ok(ctx) => ctx,
            Err(e) => return Promise::err(e),
        };

        if identity_key.len() != 32 {
            return Promise::err(coded_error(
                E004_IDENTITY_KEY_LENGTH,
                format!("identityKey must be exactly 32 bytes, got {}", identity_key.len()),
            ));
        }
        if hybrid_pk.is_empty() {
            return Promise::err(coded_error(E013_HYBRID_KEY_EMPTY, "hybridPublicKey must not be empty"));
        }

        if let Err(e) = require_identity_or_request(
            &auth_ctx,
            &identity_key,
            self.auth_cfg.allow_insecure_identity_from_request,
        ) {
            return Promise::err(e);
        }

        if let Err(e) = self
            .store
            .upload_hybrid_key(&identity_key, hybrid_pk)
            .map_err(storage_err)
        {
            return Promise::err(e);
        }

        tracing::debug!(identity = %fmt_hex(&identity_key[..4]), "hybrid public key uploaded");

        Promise::ok(())
    }

    pub fn handle_fetch_hybrid_key(
        &mut self,
        params: node_service::FetchHybridKeyParams,
        mut results: node_service::FetchHybridKeyResults,
    ) -> Promise<(), capnp::Error> {
        let p = match params.get() {
            Ok(p) => p,
            Err(e) => return Promise::err(coded_error(E020_BAD_PARAMS, e)),
        };
        let identity_key = match p.get_identity_key() {
            Ok(v) => v.to_vec(),
            Err(e) => return Promise::err(coded_error(E020_BAD_PARAMS, e)),
        };

        // Auth check only — any authenticated user can fetch any peer's hybrid public key.
        if let Err(e) = validate_auth_context(&self.auth_cfg, &self.sessions, p.get_auth()) {
            return Promise::err(e);
        }

        if identity_key.len() != 32 {
            return Promise::err(coded_error(
                E004_IDENTITY_KEY_LENGTH,
                format!("identityKey must be exactly 32 bytes, got {}", identity_key.len()),
            ));
        }

        let hybrid_pk = match self
            .store
            .fetch_hybrid_key(&identity_key)
            .map_err(storage_err)
        {
            Ok(p) => p,
            Err(e) => return Promise::err(e),
        };

        match hybrid_pk {
            Some(pk) => {
                tracing::debug!(identity = %fmt_hex(&identity_key[..4]), "hybrid key fetched");
                results.get().set_hybrid_public_key(&pk);
            }
            None => {
                tracing::debug!(identity = %fmt_hex(&identity_key[..4]), "no hybrid key for identity");
                results.get().set_hybrid_public_key(&[]);
            }
        }

        Promise::ok(())
    }

    pub fn handle_fetch_hybrid_keys(
        &mut self,
        params: node_service::FetchHybridKeysParams,
        mut results: node_service::FetchHybridKeysResults,
    ) -> Promise<(), capnp::Error> {
        let p = match params.get() {
            Ok(p) => p,
            Err(e) => return Promise::err(coded_error(E020_BAD_PARAMS, e)),
        };
        let identity_keys = match p.get_identity_keys() {
            Ok(v) => v,
            Err(e) => return Promise::err(coded_error(E020_BAD_PARAMS, e)),
        };

        if let Err(e) = validate_auth_context(&self.auth_cfg, &self.sessions, p.get_auth()) {
            return Promise::err(e);
        }

        let count = identity_keys.len() as usize;
        let mut key_data: Vec<Vec<u8>> = Vec::with_capacity(count);
        for i in 0..identity_keys.len() {
            let ik = match identity_keys.get(i) {
                Ok(v) => v.to_vec(),
                Err(e) => return Promise::err(coded_error(E020_BAD_PARAMS, e)),
            };
            let pk = match self.store.fetch_hybrid_key(&ik).map_err(storage_err) {
                Ok(Some(pk)) => pk,
                Ok(None) => vec![],
                Err(e) => return Promise::err(e),
            };
            key_data.push(pk);
        }

        let mut list = results.get().init_keys(key_data.len() as u32);
        for (i, pk) in key_data.iter().enumerate() {
            list.set(i as u32, pk);
        }

        tracing::debug!(count = count, "batch hybrid key fetch");

        Promise::ok(())
    }
}
