//! Project scanning handlers for conflict detection.

use axum::{extract::State, http::StatusCode, Json};
use std::path::PathBuf;
use tracing::instrument;

use crate::{
    dto::aphoria::{FindingDto, ListScansResponse, ScanRequest, ScanResponse, ScanSummaryDto},
    error::{ApiError, Result},
    state::AppState,
};

use super::super::aphoria_helpers::conflict_result_to_dto;

/// Run a scan on a project for conflicts.
///
/// Scans the specified project directory, extracts claims from code/config,
/// and checks them against authoritative sources. Returns 422 if fail_on_flag
/// is true and BLOCK findings exist.
#[utoipa::path(
    post,
    path = "/v1/aphoria/scan",
    request_body = ScanRequest,
    responses(
        (status = 200, description = "Scan completed (no blocking issues)", body = ScanResponse),
        (status = 422, description = "Scan found blocking issues (when fail_on_flag=true)", body = ScanResponse),
        (status = 400, description = "Invalid request", body = crate::dto::ErrorResponse),
        (status = 404, description = "Target path not found", body = crate::dto::ErrorResponse),
        (status = 500, description = "Internal server error", body = crate::dto::ErrorResponse),
    ),
    tag = "aphoria"
)]
#[instrument(skip_all, fields(target_path = %req.target_path, format = %req.format))]
pub async fn scan(
    State(state): State<AppState>,
    Json(req): Json<ScanRequest>,
) -> Result<(StatusCode, Json<ScanResponse>)> {
    let target_path = PathBuf::from(&req.target_path);

    // Check path exists
    if !target_path.exists() {
        return Err(ApiError::NotFound(format!("Target path not found: {}", req.target_path)));
    }

    // Load config from target path or default
    let config_path = target_path.join("aphoria.toml");
    let config = if config_path.exists() {
        aphoria::AphoriaConfig::from_file(&config_path)
            .map_err(|e| ApiError::Internal(format!("Failed to load config: {}", e)))?
    } else {
        aphoria::AphoriaConfig::default()
    };

    // Create scan args
    let args = aphoria::ScanArgs {
        path: target_path,
        format: req.format.clone(),
        exit_code_enabled: req.fail_on_flag,
        mode: aphoria::ScanMode::Ephemeral,
        debug: req.debug,
        sync: false,
        file_source: aphoria::FileSource::All,
        benchmark: false,
        show_claims: false,
    };

    // Execute scan
    let result = aphoria::run_scan(args, &config)
        .await
        .map_err(|e| ApiError::Internal(format!("Scan failed: {}", e)))?;

    // Check for blocks before consuming result
    let has_blocks = result.has_blocks();

    // Convert to DTO
    let findings: Vec<FindingDto> = result.conflicts.iter().map(conflict_result_to_dto).collect();

    let summary = ScanSummaryDto {
        total: result.conflicts.len(),
        blocked: result.count_by_verdict(aphoria::Verdict::Block),
        flagged: result.count_by_verdict(aphoria::Verdict::Flag),
        passed: result.count_by_verdict(aphoria::Verdict::Pass),
        acknowledged: result.count_by_verdict(aphoria::Verdict::Ack),
    };

    let response = ScanResponse {
        project: result.project,
        scan_id: result.scan_id,
        files_scanned: result.files_scanned,
        claims_extracted: result.claims_extracted,
        findings,
        summary,
    };

    // Cache the scan result
    state.scan_cache.record(&response).await;

    // Return 422 if fail_on_flag is true and there are blocks
    let status = if req.fail_on_flag && has_blocks {
        StatusCode::UNPROCESSABLE_ENTITY
    } else {
        StatusCode::OK
    };

    Ok((status, Json(response)))
}

/// List recent scan results from cache.
///
/// Returns the last 100 scans, newest first.
#[utoipa::path(
    get,
    path = "/v1/aphoria/scans",
    responses(
        (status = 200, description = "Scan history retrieved successfully", body = ListScansResponse),
        (status = 500, description = "Internal server error", body = crate::dto::ErrorResponse),
    ),
    tag = "aphoria"
)]
#[instrument(skip_all)]
pub async fn list_scans(State(state): State<AppState>) -> Result<Json<ListScansResponse>> {
    let response = state.scan_cache.list().await;
    Ok(Json(response))
}
