//! User resolution domain logic — username <-> identity key lookups.

use std::sync::{Arc, Mutex};

use quicproquo_kt::{MerkleLog, RevocationLog, RevocationReason};

use crate::storage::Store;

use super::types::*;

/// Domain service for user/identity resolution.
pub struct UserService {
    pub store: Arc<dyn Store>,
    pub kt_log: Arc<Mutex<MerkleLog>>,
    pub revocation_log: Arc<Mutex<RevocationLog>>,
}

impl UserService {
    pub fn resolve_user(&self, req: ResolveUserReq) -> Result<ResolveUserResp, DomainError> {
        if req.username.is_empty() {
            return Err(DomainError::EmptyUsername);
        }

        let identity_key = self
            .store
            .get_user_identity_key(&req.username)?
            .unwrap_or_default();

        let mut inclusion_proof = Vec::new();

        if !identity_key.is_empty() {
            if let Ok(log) = self.kt_log.lock() {
                if let Some(leaf_idx) = log.find(&req.username, &identity_key) {
                    if let Ok(proof) = log.inclusion_proof(leaf_idx) {
                        if let Ok(bytes) = proof.to_bytes() {
                            inclusion_proof = bytes;
                        }
                    }
                }
            }
        }

        Ok(ResolveUserResp {
            identity_key,
            inclusion_proof,
        })
    }

    pub fn resolve_identity(
        &self,
        req: ResolveIdentityReq,
    ) -> Result<ResolveIdentityResp, DomainError> {
        if req.identity_key.len() != 32 {
            return Err(DomainError::InvalidIdentityKey(req.identity_key.len()));
        }

        let username = self
            .store
            .resolve_identity_key(&req.identity_key)?
            .unwrap_or_default();

        Ok(ResolveIdentityResp { username })
    }

    /// Revoke an identity key in the Key Transparency log.
    pub fn revoke_key(&self, req: RevokeKeyReq) -> Result<RevokeKeyResp, DomainError> {
        if req.identity_key.len() != 32 {
            return Err(DomainError::InvalidIdentityKey(req.identity_key.len()));
        }

        let reason = RevocationReason::from_tag(&req.reason)
            .ok_or_else(|| DomainError::BadParams(format!("invalid revocation reason: {}", req.reason)))?;

        let timestamp_ms = std::time::SystemTime::now()
            .duration_since(std::time::UNIX_EPOCH)
            .map(|d| d.as_millis() as u64)
            .unwrap_or(0);

        let mut kt = self.kt_log.lock().map_err(|e| DomainError::Io(e.to_string()))?;
        let mut revlog = self.revocation_log.lock().map_err(|e| DomainError::Io(e.to_string()))?;

        let leaf_index = revlog
            .revoke(&mut kt, &req.identity_key, reason, timestamp_ms)
            .map_err(|e| DomainError::BadParams(e.to_string()))?;

        // Persist updated logs.
        if let Ok(bytes) = kt.to_bytes() {
            let _ = self.store.save_kt_log(bytes);
        }
        if let Ok(bytes) = revlog.to_bytes() {
            let _ = self.store.save_revocation_log(bytes);
        }

        Ok(RevokeKeyResp {
            success: true,
            leaf_index,
        })
    }

    /// Check if an identity key has been revoked.
    pub fn check_revocation(&self, req: CheckRevocationReq) -> Result<CheckRevocationResp, DomainError> {
        let revlog = self.revocation_log.lock().map_err(|e| DomainError::Io(e.to_string()))?;

        if let Some(entry) = revlog.get(&req.identity_key) {
            Ok(CheckRevocationResp {
                revoked: true,
                reason: entry.reason.as_tag().to_string(),
                timestamp_ms: entry.timestamp_ms,
            })
        } else {
            Ok(CheckRevocationResp {
                revoked: false,
                reason: String::new(),
                timestamp_ms: 0,
            })
        }
    }

    /// Return a range of KT log entries for client-side audit.
    pub fn audit_key_transparency(
        &self,
        req: AuditKeyTransparencyReq,
    ) -> Result<AuditKeyTransparencyResp, DomainError> {
        let kt = self.kt_log.lock().map_err(|e| DomainError::Io(e.to_string()))?;

        let end = if req.end == 0 { kt.len() } else { req.end };
        let log_entries = kt.audit_log(req.start, end);

        let entries: Vec<AuditLogEntry> = log_entries
            .into_iter()
            .map(|(index, hash)| AuditLogEntry {
                index,
                leaf_hash: hash.to_vec(),
            })
            .collect();

        let tree_size = kt.len();
        let root = kt.root().map(|r| r.to_vec()).unwrap_or_default();

        Ok(AuditKeyTransparencyResp {
            entries,
            tree_size,
            root,
        })
    }
}
