//! User resolution handlers — username <-> identity key lookups,
//! key revocation, and KT audit.

use std::sync::Arc;

use bytes::Bytes;
use prost::Message;
use quicprochat_proto::qpc::v1;
use quicprochat_rpc::method::{HandlerResult, RequestContext};

use crate::domain::types::{
    AuditKeyTransparencyReq, CheckRevocationReq, ResolveIdentityReq, ResolveUserReq, RevokeKeyReq,
};
use crate::domain::users::UserService;
use crate::federation::address::FederatedAddress;

use super::{domain_err, require_auth, ServerState};

fn user_svc(state: &Arc<ServerState>) -> UserService {
    UserService {
        store: Arc::clone(&state.store),
        kt_log: Arc::clone(&state.kt_log),
        revocation_log: Arc::clone(&state.revocation_log),
    }
}

pub async fn handle_resolve_user(state: Arc<ServerState>, ctx: RequestContext) -> HandlerResult {
    let _identity_key = match require_auth(&state, &ctx) {
        Ok(ik) => ik,
        Err(e) => return e,
    };

    let req = match v1::ResolveUserRequest::decode(ctx.payload) {
        Ok(r) => r,
        Err(e) => {
            return HandlerResult::err(
                quicprochat_rpc::error::RpcStatus::BadRequest,
                &format!("decode: {e}"),
            )
        }
    };

    // Federation identity resolution: if the username contains '@', parse it
    // and resolve only if the domain matches local or is bare.
    let addr = FederatedAddress::parse(&req.username);
    if !addr.is_local(&state.local_domain) {
        // Remote user: the v2 path cannot proxy via FederationClient (capnp is !Send).
        // Return empty to indicate the user is not local. Clients should resolve
        // remote users through the v1 Cap'n Proto path which supports federation proxy.
        tracing::info!(
            username = %addr.username,
            domain = addr.domain.as_deref().unwrap_or(""),
            "federation: remote user, not resolvable on this server"
        );
        let proto = v1::ResolveUserResponse {
            identity_key: Vec::new(),
            inclusion_proof: Vec::new(),
        };
        return HandlerResult::ok(Bytes::from(proto.encode_to_vec()));
    }

    let svc = user_svc(&state);

    let domain_req = ResolveUserReq {
        username: addr.username,
    };

    match svc.resolve_user(domain_req) {
        Ok(resp) => {
            let proto = v1::ResolveUserResponse {
                identity_key: resp.identity_key,
                inclusion_proof: resp.inclusion_proof,
            };
            HandlerResult::ok(Bytes::from(proto.encode_to_vec()))
        }
        Err(e) => domain_err(e),
    }
}

pub async fn handle_resolve_identity(
    state: Arc<ServerState>,
    ctx: RequestContext,
) -> HandlerResult {
    let _identity_key = match require_auth(&state, &ctx) {
        Ok(ik) => ik,
        Err(e) => return e,
    };

    let req = match v1::ResolveIdentityRequest::decode(ctx.payload) {
        Ok(r) => r,
        Err(e) => {
            return HandlerResult::err(
                quicprochat_rpc::error::RpcStatus::BadRequest,
                &format!("decode: {e}"),
            )
        }
    };

    let svc = user_svc(&state);

    let domain_req = ResolveIdentityReq {
        identity_key: req.identity_key,
    };

    match svc.resolve_identity(domain_req) {
        Ok(resp) => {
            let proto = v1::ResolveIdentityResponse {
                username: resp.username,
            };
            HandlerResult::ok(Bytes::from(proto.encode_to_vec()))
        }
        Err(e) => domain_err(e),
    }
}

pub async fn handle_revoke_key(state: Arc<ServerState>, ctx: RequestContext) -> HandlerResult {
    let _identity_key = match require_auth(&state, &ctx) {
        Ok(ik) => ik,
        Err(e) => return e,
    };

    let req = match v1::RevokeKeyRequest::decode(ctx.payload) {
        Ok(r) => r,
        Err(e) => {
            return HandlerResult::err(
                quicprochat_rpc::error::RpcStatus::BadRequest,
                &format!("decode: {e}"),
            )
        }
    };

    let svc = user_svc(&state);

    let domain_req = RevokeKeyReq {
        identity_key: req.identity_key,
        reason: req.reason,
    };

    match svc.revoke_key(domain_req) {
        Ok(resp) => {
            let proto = v1::RevokeKeyResponse {
                success: resp.success,
                leaf_index: resp.leaf_index,
            };
            HandlerResult::ok(Bytes::from(proto.encode_to_vec()))
        }
        Err(e) => domain_err(e),
    }
}

pub async fn handle_check_revocation(
    state: Arc<ServerState>,
    ctx: RequestContext,
) -> HandlerResult {
    let _identity_key = match require_auth(&state, &ctx) {
        Ok(ik) => ik,
        Err(e) => return e,
    };

    let req = match v1::CheckRevocationRequest::decode(ctx.payload) {
        Ok(r) => r,
        Err(e) => {
            return HandlerResult::err(
                quicprochat_rpc::error::RpcStatus::BadRequest,
                &format!("decode: {e}"),
            )
        }
    };

    let svc = user_svc(&state);

    let domain_req = CheckRevocationReq {
        identity_key: req.identity_key,
    };

    match svc.check_revocation(domain_req) {
        Ok(resp) => {
            let proto = v1::CheckRevocationResponse {
                revoked: resp.revoked,
                reason: resp.reason,
                timestamp_ms: resp.timestamp_ms,
            };
            HandlerResult::ok(Bytes::from(proto.encode_to_vec()))
        }
        Err(e) => domain_err(e),
    }
}

pub async fn handle_audit_key_transparency(
    state: Arc<ServerState>,
    ctx: RequestContext,
) -> HandlerResult {
    let _identity_key = match require_auth(&state, &ctx) {
        Ok(ik) => ik,
        Err(e) => return e,
    };

    let req = match v1::AuditKeyTransparencyRequest::decode(ctx.payload) {
        Ok(r) => r,
        Err(e) => {
            return HandlerResult::err(
                quicprochat_rpc::error::RpcStatus::BadRequest,
                &format!("decode: {e}"),
            )
        }
    };

    let svc = user_svc(&state);

    let domain_req = AuditKeyTransparencyReq {
        start: req.start,
        end: req.end,
    };

    match svc.audit_key_transparency(domain_req) {
        Ok(resp) => {
            let proto = v1::AuditKeyTransparencyResponse {
                entries: resp
                    .entries
                    .into_iter()
                    .map(|e| v1::LogEntry {
                        index: e.index,
                        leaf_hash: e.leaf_hash,
                    })
                    .collect(),
                tree_size: resp.tree_size,
                root: resp.root,
            };
            HandlerResult::ok(Bytes::from(proto.encode_to_vec()))
        }
        Err(e) => domain_err(e),
    }
}
