//! API handlers for gold standard operations.

use axum::{
    extract::{Path, State},
    http::StatusCode,
    Json,
};
use tracing::instrument;

use crate::{
    dto::{
        CreateGoldStandardRequest, CreateGoldStandardResponse, GoldStandardDto,
        GoldStandardListResponse, VerificationResult, VerifyAgentRequest,
    },
    error::{ApiError, Result},
    state::AppState,
};
use std::sync::Arc;
use stemedb_core::types::GoldStandard;
use stemedb_storage::{
    GenericGoldStandardStore, GenericTrustRankStore, GoldStandardStore, TrustRankStore,
};

/// Create a new gold standard.
///
/// Only administrators should have access to this endpoint. The gold standard
/// references an existing assertion that has been verified as ground truth.
#[utoipa::path(
    post,
    path = "/v1/admin/gold-standards",
    request_body = CreateGoldStandardRequest,
    responses(
        (status = 201, description = "Gold standard created", body = CreateGoldStandardResponse),
        (status = 400, description = "Invalid request", body = crate::dto::ErrorResponse),
        (status = 500, description = "Internal server error", body = crate::dto::ErrorResponse),
    ),
    tag = "admin"
)]
#[instrument(skip(state), fields(subject = %req.subject, predicate = %req.predicate))]
pub async fn create_gold_standard(
    State(state): State<AppState>,
    Json(req): Json<CreateGoldStandardRequest>,
) -> Result<(StatusCode, Json<CreateGoldStandardResponse>)> {
    // Validate input lengths
    use stemedb_core::limits::{MAX_OBJECT_LEN, MAX_PREDICATE_LEN, MAX_SUBJECT_LEN};
    if req.subject.len() > MAX_SUBJECT_LEN {
        return Err(ApiError::InvalidRequest(format!(
            "subject exceeds maximum length of {} bytes",
            MAX_SUBJECT_LEN
        )));
    }
    if req.predicate.len() > MAX_PREDICATE_LEN {
        return Err(ApiError::InvalidRequest(format!(
            "predicate exceeds maximum length of {} bytes",
            MAX_PREDICATE_LEN
        )));
    }
    if req.expected_object.len() > MAX_OBJECT_LEN {
        return Err(ApiError::InvalidRequest(format!(
            "expected_object exceeds maximum length of {} bytes",
            MAX_OBJECT_LEN
        )));
    }

    // Decode assertion hash
    let assertion_hash = hex::decode(&req.assertion_hash)
        .map_err(|e| ApiError::InvalidRequest(format!("Invalid assertion_hash hex: {}", e)))?;
    if assertion_hash.len() != 32 {
        return Err(ApiError::InvalidRequest("assertion_hash must be 32 bytes".to_string()));
    }
    let mut hash_array = [0u8; 32];
    hash_array.copy_from_slice(&assertion_hash);

    // Get current timestamp
    let timestamp = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map(|d| d.as_secs())
        .unwrap_or(0);

    // Create the gold standard
    let gs = GoldStandard::new(
        hash_array,
        req.subject.clone(),
        req.predicate.clone(),
        req.expected_object,
        timestamp,
        req.created_by,
    );

    // Store it
    let gs_store = GenericGoldStandardStore::new(Arc::clone(&state.store));
    gs_store.set_gold_standard(&gs).await?;

    Ok((
        StatusCode::CREATED,
        Json(CreateGoldStandardResponse {
            subject: req.subject,
            predicate: req.predicate,
            status: "Gold standard created".to_string(),
        }),
    ))
}

/// List all gold standards.
#[utoipa::path(
    get,
    path = "/v1/admin/gold-standards",
    responses(
        (status = 200, description = "Gold standards retrieved", body = GoldStandardListResponse),
        (status = 500, description = "Internal server error", body = crate::dto::ErrorResponse),
    ),
    tag = "admin"
)]
#[instrument(skip(state))]
pub async fn list_gold_standards(
    State(state): State<AppState>,
) -> Result<Json<GoldStandardListResponse>> {
    let gs_store = GenericGoldStandardStore::new(Arc::clone(&state.store));
    let gold_standards = gs_store.list_gold_standards().await?;

    let dtos: Vec<GoldStandardDto> =
        gold_standards.into_iter().map(GoldStandardDto::from).collect();

    Ok(Json(GoldStandardListResponse { count: dtos.len(), gold_standards: dtos }))
}

/// Remove a gold standard.
#[utoipa::path(
    delete,
    path = "/v1/admin/gold-standards/{subject}/{predicate}",
    params(
        ("subject" = String, Path, description = "Subject entity"),
        ("predicate" = String, Path, description = "Predicate"),
    ),
    responses(
        (status = 200, description = "Gold standard removed or not found"),
        (status = 500, description = "Internal server error", body = crate::dto::ErrorResponse),
    ),
    tag = "admin"
)]
#[instrument(skip(state), fields(subject = %subject, predicate = %predicate))]
pub async fn remove_gold_standard(
    State(state): State<AppState>,
    Path((subject, predicate)): Path<(String, String)>,
) -> Result<Json<serde_json::Value>> {
    let gs_store = GenericGoldStandardStore::new(Arc::clone(&state.store));
    let removed = gs_store.remove_gold_standard(&subject, &predicate).await?;

    let status = if removed { "Gold standard removed" } else { "Gold standard not found" };

    Ok(Json(serde_json::json!({
        "subject": subject,
        "predicate": predicate,
        "removed": removed,
        "status": status,
    })))
}

/// Verify an agent against a gold standard.
///
/// This endpoint checks if the agent's answer matches the gold standard's
/// expected object. The agent's TrustRank is updated accordingly:
/// - Correct answer: +0.05 trust
/// - Incorrect answer: -0.1 trust
#[utoipa::path(
    post,
    path = "/v1/admin/verify-agent",
    request_body = VerifyAgentRequest,
    responses(
        (status = 200, description = "Agent verified", body = VerificationResult),
        (status = 400, description = "Invalid request", body = crate::dto::ErrorResponse),
        (status = 404, description = "Gold standard not found", body = crate::dto::ErrorResponse),
        (status = 409, description = "Agent already verified this gold standard", body = crate::dto::ErrorResponse),
        (status = 500, description = "Internal server error", body = crate::dto::ErrorResponse),
    ),
    tag = "admin"
)]
#[instrument(skip(state), fields(
    agent_id = %req.agent_id,
    subject = %req.subject,
    predicate = %req.predicate
))]
pub async fn verify_agent(
    State(state): State<AppState>,
    Json(req): Json<VerifyAgentRequest>,
) -> Result<Json<VerificationResult>> {
    // Validate input lengths
    use stemedb_core::limits::{MAX_OBJECT_LEN, MAX_PREDICATE_LEN, MAX_SUBJECT_LEN};
    if req.subject.len() > MAX_SUBJECT_LEN {
        return Err(ApiError::InvalidRequest(format!(
            "subject exceeds maximum length of {} bytes",
            MAX_SUBJECT_LEN
        )));
    }
    if req.predicate.len() > MAX_PREDICATE_LEN {
        return Err(ApiError::InvalidRequest(format!(
            "predicate exceeds maximum length of {} bytes",
            MAX_PREDICATE_LEN
        )));
    }
    if req.agent_object.len() > MAX_OBJECT_LEN {
        return Err(ApiError::InvalidRequest(format!(
            "agent_object exceeds maximum length of {} bytes",
            MAX_OBJECT_LEN
        )));
    }

    // Decode agent ID
    let agent_id_bytes = hex::decode(&req.agent_id)
        .map_err(|e| ApiError::InvalidRequest(format!("Invalid agent_id hex: {}", e)))?;
    if agent_id_bytes.len() != 32 {
        return Err(ApiError::InvalidRequest("agent_id must be 32 bytes".to_string()));
    }
    let mut agent_id = [0u8; 32];
    agent_id.copy_from_slice(&agent_id_bytes);

    // Get the gold standard
    let gs_store = GenericGoldStandardStore::new(Arc::clone(&state.store));
    let gs = gs_store.get_gold_standard(&req.subject, &req.predicate).await?.ok_or_else(|| {
        ApiError::NotFound(format!("Gold standard not found for {}:{}", req.subject, req.predicate))
    })?;

    // Get current timestamp
    let timestamp = std::time::SystemTime::now()
        .duration_since(std::time::UNIX_EPOCH)
        .map(|d| d.as_secs())
        .unwrap_or(0);

    // Verify the agent
    let trust_store = GenericTrustRankStore::new(state.store.clone());
    let adjustment = trust_store
        .verify_agent_against_gold_standard(&agent_id, &req.agent_object, &gs, timestamp)
        .await?;

    // If already verified, return 409 Conflict
    if adjustment.is_already_verified() {
        return Err(ApiError::Conflict(format!(
            "Agent {} has already verified gold standard {}:{}",
            req.agent_id, req.subject, req.predicate
        )));
    }

    // Get updated trust rank
    let trust_rank = trust_store.get_trust_rank(&agent_id).await?;

    Ok(Json(VerificationResult {
        subject: req.subject,
        predicate: req.predicate,
        correct: adjustment.is_reward(),
        agent_answer: req.agent_object,
        expected_answer: gs.expected_object,
        trust_adjustment: adjustment.delta(),
        new_trust_score: trust_rank.score,
    }))
}
