//! Inference Rules Management Handlers

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 to avoid duplication
pub use crate::services::iac_rules::{InferenceRule, RuleInference};
use crate::{AppState, Result};

/// Paginated response
#[derive(Serialize)]
pub struct PagedResponse<T> {
    pub items: Vec<T>,
    pub total: u32,
    pub page: u32,
    pub page_size: u32,
}

/// Query parameters
#[derive(Deserialize)]
pub struct RulesQuery {
    pub page: Option<u32>,
    pub page_size: Option<u32>,
    pub org: Option<String>,
    pub enabled: Option<bool>,
}

/// List all inference rules
#[onto_api(
    method = "GET",
    path = "/rules",
    description = "List all inference rules",
    auth = "bearer",
    actors = "developer",
    tags = "rules",
    feature = ""
)]
pub async fn list_rules(
    State(state): State<Arc<AppState>>,
    Query(params): Query<RulesQuery>,
) -> Result<Json<PagedResponse<InferenceRule>>> {
    let page = params.page.unwrap_or(1);
    let page_size = params.page_size.unwrap_or(20);

    let (items, total) = state
        .iac_rules_service
        .list_rules(page, page_size, params.enabled, params.org)
        .await?;

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

/// List rules for specific organization
#[onto_api(
    method = "GET",
    path = "/rules/org/{org}",
    description = "List inference rules for a specific organization",
    auth = "bearer",
    actors = "developer",
    tags = "rules",
    feature = ""
)]
pub async fn list_org_rules(
    State(state): State<Arc<AppState>>,
    Path(org): Path<String>,
    Query(params): Query<RulesQuery>,
) -> Result<Json<PagedResponse<InferenceRule>>> {
    let page = params.page.unwrap_or(1);
    let page_size = params.page_size.unwrap_or(20);

    let (items, total) = state
        .iac_rules_service
        .list_rules(page, page_size, params.enabled, Some(org))
        .await?;

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

/// Get single rule
#[onto_api(
    method = "GET",
    path = "/rules/{id}",
    description = "Get an inference rule by ID",
    auth = "bearer",
    actors = "developer",
    tags = "rules",
    feature = ""
)]
pub async fn get_rule(
    State(state): State<Arc<AppState>>,
    Path(id): Path<String>,
) -> Result<Json<InferenceRule>> {
    state.iac_rules_service.get_rule(&id).await.map(Json)
}

/// Create new rule
#[onto_api(
    method = "POST",
    path = "/rules",
    description = "Create a new inference rule",
    auth = "bearer",
    actors = "developer",
    tags = "rules",
    feature = ""
)]
pub async fn create_rule(
    State(state): State<Arc<AppState>>,
    Json(rule): Json<InferenceRule>,
) -> Result<(StatusCode, Json<InferenceRule>)> {
    let created = state.iac_rules_service.create_rule(rule).await?;
    Ok((StatusCode::CREATED, Json(created)))
}

/// Update existing rule
#[onto_api(
    method = "PUT",
    path = "/rules/{id}",
    description = "Update an inference rule",
    auth = "bearer",
    actors = "developer",
    tags = "rules",
    feature = ""
)]
pub async fn update_rule(
    State(state): State<Arc<AppState>>,
    Path(id): Path<String>,
    Json(rule): Json<InferenceRule>,
) -> Result<Json<InferenceRule>> {
    let updated = state.iac_rules_service.update_rule(&id, rule).await?;
    Ok(Json(updated))
}

/// Delete rule
#[onto_api(
    method = "DELETE",
    path = "/rules/{id}",
    description = "Delete an inference rule",
    auth = "bearer",
    actors = "developer",
    tags = "rules",
    feature = ""
)]
pub async fn delete_rule(
    State(state): State<Arc<AppState>>,
    Path(id): Path<String>,
) -> Result<StatusCode> {
    state.iac_rules_service.delete_rule(&id).await?;
    Ok(StatusCode::NO_CONTENT)
}

/// Test rule against project
#[derive(Deserialize)]
pub struct TestRuleRequest {
    pub project_path: String,
}

#[derive(Serialize)]
pub struct TestRuleResult {
    pub matches: bool,
    pub triggered_technologies: Vec<String>,
    pub inferred_services: Vec<String>,
    pub confidence_score: f32,
}

#[onto_api(
    method = "POST",
    path = "/rules/{id}/test",
    description = "Test an inference rule against a project",
    auth = "bearer",
    actors = "developer",
    tags = "rules",
    feature = ""
)]
pub async fn test_rule(
    State(state): State<Arc<AppState>>,
    Path(id): Path<String>,
    Json(_req): Json<TestRuleRequest>,
) -> Result<Json<TestRuleResult>> {
    // Retrieve the rule
    let _rule = state.iac_rules_service.get_rule(&id).await?;

    // TODO: Call provisioning-detector on the project path
    // TODO: Apply rule logic to detected technologies
    // For now, return placeholder response
    Ok(Json(TestRuleResult {
        matches: false,
        triggered_technologies: vec![],
        inferred_services: vec![],
        confidence_score: 0.0,
    }))
}

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

    use super::*;

    #[test]
    fn test_inference_rule_creation() {
        let rule = InferenceRule {
            id: Uuid::new_v4().to_string(),
            name: "Test Rule".to_string(),
            organization: "default".to_string(),
            triggers: vec!["python".to_string()],
            inferences: vec![RuleInference {
                technology: "postgresql".to_string(),
                reason: "Python apps usually need a database".to_string(),
                confidence: 0.85,
                required: true,
            }],
            enabled: true,
            priority: 10,
            created_at: Utc::now().to_rfc3339(),
            updated_at: Utc::now().to_rfc3339(),
        };

        assert_eq!(rule.name, "Test Rule");
        assert_eq!(rule.triggers.len(), 1);
        assert_eq!(rule.inferences.len(), 1);
    }
}
