use crate::models::AnalysisResult;
use crate::{AppState, ServerError};
use axum::{
    extract::{Path, Query, State},
    http::StatusCode,
    response::IntoResponse,
    Json,
};
use common_contracts::dtos::{AnalysisResultDto, NewAnalysisResult};
use serde::Deserialize;
use service_kit::api;
use tracing::{instrument, error};
use anyhow::Error as AnyhowError;
use uuid::Uuid;

#[derive(Debug, Deserialize, utoipa::IntoParams, utoipa::ToSchema)]
pub struct AnalysisQuery {
    pub symbol: String,
    pub module_id: Option<String>,
}

/// Creates a new analysis result and returns the created record.
#[instrument(skip(state, payload), fields(request_id = %payload.request_id, symbol = %payload.symbol, module_id = %payload.module_id))]
#[api(POST, "/api/v1/analysis-results")]
pub async fn create_analysis_result(
    State(state): State<AppState>,
    Json(payload): Json<NewAnalysisResult>,
) -> Result<impl IntoResponse, ServerError> {
    let result = crate::db::create_analysis_result(state.pool(), &payload)
        .await
        .map_err(|e| {
            error!("Database error inserting analysis result: {}", e);
            AnyhowError::from(e)
        })?;

    let dto = AnalysisResultDto {
        id: result.id,
        request_id: result.request_id,
        symbol: result.symbol,
        template_id: result.template_id,
        module_id: result.module_id,
        content: result.content,
        meta_data: result.meta_data,
        created_at: result.created_at,
    };

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

/// Retrieves all analysis results for a given symbol.
#[instrument(skip(state))]
#[api(GET, "/api/v1/analysis-results", output(list = "AnalysisResultDto"))]
pub async fn get_analysis_results(
    State(state): State<AppState>,
    Query(query): Query<AnalysisQuery>,
) -> Result<Json<Vec<AnalysisResultDto>>, ServerError> {
    let results = crate::db::get_analysis_results(state.pool(), &query.symbol, query.module_id.as_deref())
        .await
        .map_err(|e| {
            error!("Database error fetching analysis results: {}", e);
            AnyhowError::from(e)
        })?;

    let dtos = results
        .into_iter()
        .map(|r| AnalysisResultDto {
            id: r.id,
            request_id: r.request_id,
            symbol: r.symbol,
            template_id: r.template_id,
            module_id: r.module_id,
            content: r.content,
            meta_data: r.meta_data,
            created_at: r.created_at,
        })
        .collect();

    Ok(Json(dtos))
}

/// Retrieves a single analysis result by its primary ID.
#[instrument(skip(state))]
#[api(GET, "/api/v1/analysis-results/{id}", output(detail = "AnalysisResultDto"))]
pub async fn get_analysis_result_by_id(
    State(state): State<AppState>,
    Path(id): Path<String>,
) -> Result<Json<AnalysisResultDto>, ServerError> {
    let id = Uuid::parse_str(&id).map_err(|_| ServerError::NotFound(format!("Invalid UUID: {}", id)))?;

    let result = crate::db::get_analysis_result_by_id(state.pool(), id)
        .await
        .map_err(|e| {
            error!("Database error fetching analysis result by id: {}", e);
            AnyhowError::from(e)
        })?
        .ok_or_else(|| ServerError::NotFound(format!("Analysis result not found: {}", id)))?;

    let dto = AnalysisResultDto {
        id: result.id,
        request_id: result.request_id,
        symbol: result.symbol,
        template_id: result.template_id,
        module_id: result.module_id,
        content: result.content,
        meta_data: result.meta_data,
        created_at: result.created_at,
    };

    Ok(Json(dto))
}
