//! Infrastructure-from-Code Detection Handlers
//!
//! Handlers for technology detection, completeness analysis, and infrastructure
//! inference from source code analysis.

use std::sync::Arc;

use axum::{
    extract::{Path, Query, State},
    http::StatusCode,
    Json,
};
use ontoref_derive::onto_api;
use serde::{Deserialize, Serialize};

// Re-export service types
pub use crate::services::iac_detection::DetectedTechnology;
use crate::{AppState, Result};

/// Detection result formatted for handler responses
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetectionResult {
    pub id: String,
    pub project: String,
    pub timestamp: String,
    pub detections: Vec<DetectedTechnology>,
    pub completeness: f32,
    pub confidence: f32,
}

/// Query parameters for listing detections
#[derive(Deserialize)]
pub struct DetectionQuery {
    pub page: Option<u32>,
    pub page_size: Option<u32>,
    pub project: Option<String>,
}

/// Response wrapper for paginated results
#[derive(Serialize)]
pub struct PagedResponse<T> {
    pub items: Vec<T>,
    pub total: u32,
    pub page: u32,
    pub page_size: u32,
}

/// List all detection results
#[onto_api(
    method = "GET",
    path = "/detections",
    description = "List all infrastructure detections",
    auth = "bearer",
    actors = "developer, agent",
    tags = "detections",
    feature = ""
)]
pub async fn list_detections(
    State(state): State<Arc<AppState>>,
    Query(params): Query<DetectionQuery>,
) -> Result<Json<PagedResponse<DetectionResult>>> {
    let page = params.page.unwrap_or(1);
    let page_size = params.page_size.unwrap_or(20);

    let (results, total) = state
        .iac_detection_service
        .list_detections(page, page_size, None)
        .await?;

    // Map service results to handler response format
    let items = results
        .into_iter()
        .map(|r| DetectionResult {
            id: r.id,
            project: r.project_path,
            timestamp: r.created_at,
            detections: r.technologies,
            completeness: r.completeness,
            confidence: r.confidence,
        })
        .collect();

    Ok(Json(PagedResponse {
        items,
        total,
        page,
        page_size,
    }))
}

/// Get single detection by ID
#[onto_api(
    method = "GET",
    path = "/detections/{id}",
    description = "Get infrastructure detection by ID",
    auth = "bearer",
    actors = "developer, agent",
    tags = "detections",
    feature = ""
)]
pub async fn get_detection(
    State(state): State<Arc<AppState>>,
    Path(id): Path<String>,
) -> Result<Json<DetectionResult>> {
    let result = state.iac_detection_service.get_detection(&id).await?;

    let detection = DetectionResult {
        id: result.id,
        project: result.project_path,
        timestamp: result.created_at,
        detections: result.technologies,
        completeness: result.completeness,
        confidence: result.confidence,
    };

    Ok(Json(detection))
}

/// Analyze project for technologies
#[derive(Deserialize)]
pub struct AnalyzeProjectRequest {
    pub project_path: String,
    pub organization: Option<String>,
}

#[onto_api(
    method = "POST",
    path = "/detections/analyze",
    description = "Analyze a project for infrastructure technologies",
    auth = "bearer",
    actors = "developer, agent",
    tags = "detections",
    feature = ""
)]
pub async fn analyze_project(
    State(state): State<Arc<AppState>>,
    Json(req): Json<AnalyzeProjectRequest>,
) -> Result<(StatusCode, Json<DetectionResult>)> {
    // Run detector analysis on project
    let detector = crate::services::detector::DetectorService::new(None);
    let mut detection_result = detector.analyze_and_convert(&req.project_path)?;

    // Set organization
    detection_result.organization = req.organization.unwrap_or_else(|| "default".to_string());

    // Store in database
    let created = state
        .iac_detection_service
        .create_detection(detection_result)
        .await?;

    // Convert to handler response format
    let detection = DetectionResult {
        id: created.id,
        project: created.project_path,
        timestamp: created.created_at,
        detections: created.technologies,
        completeness: created.completeness,
        confidence: created.confidence,
    };

    Ok((StatusCode::CREATED, Json(detection)))
}

/// Detailed technology detection result
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DetailedDetection {
    pub detection: DetectionResult,
    pub analysis_metadata: AnalysisMetadata,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalysisMetadata {
    pub analyzed_at: String,
    pub analysis_duration_ms: u32,
    pub files_scanned: u32,
    pub confidence_distribution: ConfidenceDistribution,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConfidenceDistribution {
    pub high: u32,   // >= 90%
    pub medium: u32, // 70-89%
    pub low: u32,    // < 70%
}

/// Get detailed analysis for a detection
pub async fn get_detection_details(
    State(_state): State<Arc<AppState>>,
    Path(_id): Path<String>,
) -> Result<Json<DetailedDetection>> {
    // TODO: Fetch from database with additional analysis
    Err(anyhow::anyhow!("Detection not found").into())
}

#[cfg(test)]
mod tests {
    use chrono::Utc;

    use super::*;

    #[test]
    fn test_detection_result_serialization() {
        let detection = DetectionResult {
            id: "test-id".to_string(),
            project: "test-project".to_string(),
            timestamp: Utc::now().to_rfc3339(),
            detections: vec![DetectedTechnology {
                technology: "PostgreSQL".to_string(),
                confidence: 0.95,
                evidence: vec!["docker-compose.yml".to_string()],
                version: Some("14.0".to_string()),
            }],
            completeness: 0.72,
            confidence: 0.89,
        };

        let json = serde_json::to_string(&detection).expect("serialization failed");
        assert!(json.contains("PostgreSQL"));
        assert!(json.contains("0.95"));
    }

    #[test]
    fn test_paged_response() {
        let response: PagedResponse<DetectionResult> = PagedResponse {
            items: vec![],
            total: 0,
            page: 1,
            page_size: 20,
        };

        assert_eq!(response.page, 1);
        assert_eq!(response.page_size, 20);
    }
}
