//! Moderation handlers — report, ban, unban, list reports, list banned.

use std::sync::Arc;

use bytes::Bytes;
use prost::Message;
use quicproquo_proto::qpq::v1;
use quicproquo_rpc::error::RpcStatus;
use quicproquo_rpc::method::{HandlerResult, RequestContext};
use tracing::{info, warn};

use super::{require_auth, BanRecord, ModerationReport, ServerState};

/// Submit an encrypted report. Any authenticated user can report.
pub async fn handle_report_message(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::ReportMessageRequest::decode(ctx.payload) {
        Ok(r) => r,
        Err(e) => return HandlerResult::err(RpcStatus::BadRequest, &format!("decode: {e}")),
    };

    if req.encrypted_report.is_empty() {
        return HandlerResult::err(RpcStatus::BadRequest, "encrypted_report required");
    }

    let now = crate::auth::current_timestamp();
    let report = {
        let mut reports = match state.moderation_reports.lock() {
            Ok(r) => r,
            Err(e) => {
                warn!("moderation_reports lock poisoned: {e}");
                return HandlerResult::err(RpcStatus::Internal, "internal error");
            }
        };
        let id = reports.len() as u64;
        let report = ModerationReport {
            id,
            encrypted_report: req.encrypted_report,
            conversation_id: req.conversation_id,
            reporter_identity: identity_key.clone(),
            timestamp: now,
        };
        reports.push(report.clone());
        report
    };

    info!(
        report_id = report.id,
        reporter = hex::encode(&identity_key[..4.min(identity_key.len())]),
        "moderation report submitted"
    );

    let proto = v1::ReportMessageResponse { accepted: true };
    HandlerResult::ok(Bytes::from(proto.encode_to_vec()))
}

/// Ban a user. Requires admin role (currently: any authenticated user for MVP).
pub async fn handle_ban_user(state: Arc<ServerState>, ctx: RequestContext) -> HandlerResult {
    let admin_key = match require_auth(&state, &ctx) {
        Ok(ik) => ik,
        Err(e) => return e,
    };

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

    if req.identity_key.is_empty() || req.identity_key.len() != 32 {
        return HandlerResult::err(RpcStatus::BadRequest, "identity_key must be 32 bytes");
    }

    let now = crate::auth::current_timestamp();
    let expires_at = if req.duration_secs == 0 {
        0 // permanent
    } else {
        now + req.duration_secs
    };

    let record = BanRecord {
        reason: req.reason.clone(),
        banned_at: now,
        expires_at,
    };
    state.banned_users.insert(req.identity_key.clone(), record);

    info!(
        target_key = hex::encode(&req.identity_key[..4]),
        admin_key = hex::encode(&admin_key[..4.min(admin_key.len())]),
        reason = %req.reason,
        duration_secs = req.duration_secs,
        "user banned"
    );

    let proto = v1::BanUserResponse { success: true };
    HandlerResult::ok(Bytes::from(proto.encode_to_vec()))
}

/// Unban a user. Requires admin role.
pub async fn handle_unban_user(state: Arc<ServerState>, ctx: RequestContext) -> HandlerResult {
    let admin_key = match require_auth(&state, &ctx) {
        Ok(ik) => ik,
        Err(e) => return e,
    };

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

    if req.identity_key.is_empty() {
        return HandlerResult::err(RpcStatus::BadRequest, "identity_key required");
    }

    let removed = state.banned_users.remove(&req.identity_key).is_some();

    info!(
        target_key = hex::encode(&req.identity_key[..4.min(req.identity_key.len())]),
        admin_key = hex::encode(&admin_key[..4.min(admin_key.len())]),
        removed,
        "user unbanned"
    );

    let proto = v1::UnbanUserResponse { success: removed };
    HandlerResult::ok(Bytes::from(proto.encode_to_vec()))
}

/// List moderation reports. Requires admin role.
pub async fn handle_list_reports(state: Arc<ServerState>, ctx: RequestContext) -> HandlerResult {
    let _admin_key = match require_auth(&state, &ctx) {
        Ok(ik) => ik,
        Err(e) => return e,
    };

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

    let reports = match state.moderation_reports.lock() {
        Ok(r) => r,
        Err(e) => {
            warn!("moderation_reports lock poisoned: {e}");
            return HandlerResult::err(RpcStatus::Internal, "internal error");
        }
    };

    let offset = req.offset as usize;
    let limit = if req.limit == 0 { 50 } else { req.limit as usize };

    let entries: Vec<v1::ReportEntry> = reports
        .iter()
        .skip(offset)
        .take(limit)
        .map(|r| v1::ReportEntry {
            id: r.id,
            encrypted_report: r.encrypted_report.clone(),
            conversation_id: r.conversation_id.clone(),
            reporter_identity: r.reporter_identity.clone(),
            timestamp: r.timestamp,
        })
        .collect();

    let proto = v1::ListReportsResponse { reports: entries };
    HandlerResult::ok(Bytes::from(proto.encode_to_vec()))
}

/// List banned users.
pub async fn handle_list_banned(state: Arc<ServerState>, ctx: RequestContext) -> HandlerResult {
    let _admin_key = match require_auth(&state, &ctx) {
        Ok(ik) => ik,
        Err(e) => return e,
    };

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

    let now = crate::auth::current_timestamp();
    let entries: Vec<v1::BannedUserEntry> = state
        .banned_users
        .iter()
        .filter(|entry| entry.expires_at == 0 || entry.expires_at > now)
        .map(|entry| v1::BannedUserEntry {
            identity_key: entry.key().clone(),
            reason: entry.reason.clone(),
            banned_at: entry.banned_at,
            expires_at: entry.expires_at,
        })
        .collect();

    let proto = v1::ListBannedResponse { users: entries };
    HandlerResult::ok(Bytes::from(proto.encode_to_vec()))
}
