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

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

use super::NodeServiceImpl;

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

impl NodeServiceImpl {
    /// Health check: unauthenticated by design for liveness probes and load balancers.
    pub fn handle_health(
        &mut self,
        _params: node_service::HealthParams,
        mut results: node_service::HealthResults,
    ) -> Promise<(), capnp::Error> {
        results.get().set_status("ok");
        Promise::ok(())
    }

    pub fn handle_publish_endpoint(
        &mut self,
        params: node_service::PublishEndpointParams,
        _results: node_service::PublishEndpointResults,
    ) -> 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 node_addr = match p.get_node_addr() {
            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 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
            .publish_endpoint(&identity_key, node_addr)
            .map_err(storage_err)
        {
            return Promise::err(e);
        }

        tracing::debug!(identity = %fmt_hex(&identity_key[..4]), "endpoint published");

        Promise::ok(())
    }

    pub fn handle_resolve_endpoint(
        &mut self,
        params: node_service::ResolveEndpointParams,
        mut results: node_service::ResolveEndpointResults,
    ) -> 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)),
        };
        if let Err(e) = validate_auth(&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 endpoint = match self
            .store
            .resolve_endpoint(&identity_key)
            .map_err(storage_err)
        {
            Ok(e) => e,
            Err(e) => return Promise::err(e),
        };

        if let Some(ep) = endpoint {
            tracing::debug!(identity = %fmt_hex(&identity_key[..4]), "endpoint resolved");
            results.get().set_node_addr(&ep);
        } else {
            results.get().set_node_addr(&[]);
        }

        Promise::ok(())
    }
}
