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 tracing::instrument;
use anyhow::Error as AnyhowError;

#[derive(Debug, Deserialize)]
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))]
pub async fn create_analysis_result(
    State(state): State<AppState>,
    Json(payload): Json<NewAnalysisResult>,
) -> Result<impl IntoResponse, ServerError> {
    let result = sqlx::query_as::<_, AnalysisResult>(
        r#"
        INSERT INTO analysis_results (request_id, symbol, template_id, module_id, content, meta_data)
        VALUES ($1, $2, $3, $4, $5, $6)
        RETURNING *
        "#
    )
    .bind(&payload.request_id)
    .bind(&payload.symbol)
    .bind(&payload.template_id)
    .bind(&payload.module_id)
    .bind(&payload.content)
    .bind(&payload.meta_data)
    .fetch_one(state.pool())
    .await
    .map_err(AnyhowError::from)?;

    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))]
pub async fn get_analysis_results(
    State(state): State<AppState>,
    Query(query): Query<AnalysisQuery>,
) -> Result<Json<Vec<AnalysisResultDto>>, ServerError> {
    let results = sqlx::query_as::<_, AnalysisResult>(
        r#"
        SELECT * FROM analysis_results
        WHERE symbol = $1
        ORDER BY created_at DESC
        "#
    )
    .bind(&query.symbol)
    .fetch_all(state.pool())
    .await
    .map_err(AnyhowError::from)?;

    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))]
pub async fn get_analysis_result_by_id(
    State(state): State<AppState>,
    Path(id): Path<i64>,
) -> Result<Json<AnalysisResultDto>, ServerError> {
    let result = sqlx::query_as::<_, AnalysisResult>(
        r#"
        SELECT * FROM analysis_results
        WHERE id = $1
        "#
    )
    .bind(&id)
    .fetch_one(state.pool())
    .await
    .map_err(AnyhowError::from)?;

    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))
}
