use crate::error::Result;
use crate::state::AppState;
use axum::{
    extract::{Path, State},
    http::StatusCode,
    response::{IntoResponse, Json},
    routing::{get, post},
    Router,
};
use common_contracts::messages::{FetchCompanyDataCommand, GenerateReportCommand};
use common_contracts::observability::{HealthStatus, ServiceStatus, TaskProgress};
use futures_util::future::join_all;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use tracing::{info, warn};
use uuid::Uuid;

const DATA_FETCH_QUEUE: &str = "data_fetch_commands";
const ANALYSIS_COMMANDS_QUEUE: &str = "analysis.commands.generate_report";

// --- Request/Response Structs ---
#[derive(Deserialize)]
pub struct DataRequest {
    pub symbol: String,
    pub market: String,
}

#[derive(Serialize)]
pub struct RequestAcceptedResponse {
    pub request_id: Uuid,
}

#[derive(Deserialize)]
pub struct AnalysisRequest {
    pub template_id: String,
}

// --- Router Definition ---
pub fn create_router(app_state: AppState) -> Router {
    Router::new()
        .route("/health", get(health_check))
        .route("/tasks", get(get_current_tasks)) // This is the old, stateless one
        .nest("/v1", create_v1_router())
        .with_state(app_state)
}

fn create_v1_router() -> Router<AppState> {
    Router::new()
        .route("/data-requests", post(trigger_data_fetch))
        .route(
            "/analysis-requests/{symbol}",
            post(trigger_analysis_generation),
        )
        .route("/companies/{symbol}/profile", get(get_company_profile))
        .route("/tasks/{request_id}", get(get_task_progress))
        // --- New Config Routes ---
        .route(
            "/configs/llm_providers",
            get(get_llm_providers_config).put(update_llm_providers_config),
        )
        .route(
            "/configs/analysis_template_sets",
            get(get_analysis_template_sets).put(update_analysis_template_sets),
        )
        .route(
            "/configs/data_sources",
            get(get_data_sources_config).put(update_data_sources_config),
        )
        .route("/configs/test", post(test_data_source_config))
        // --- New Discover Routes ---
        .route("/discover-models/{provider_id}", get(discover_models))
        .route("/discover-models", post(discover_models_preview))
}

// --- Health & Stateless Tasks ---
async fn health_check(State(state): State<AppState>) -> Json<HealthStatus> {
    let mut details = HashMap::new();
    // 提供确定性且无副作用的健康详情，避免访问不存在的状态字段
    details.insert("message_bus".to_string(), "nats".to_string());
    details.insert("nats_addr".to_string(), state.config.nats_addr.clone());
    
    let status = HealthStatus {
        module_id: "api-gateway".to_string(),
        status: ServiceStatus::Ok,
        version: env!("CARGO_PKG_VERSION").to_string(),
        details,
    };
    Json(status)
}
async fn get_current_tasks() -> Json<Vec<TaskProgress>> {
    Json(vec![])
}

// --- V1 API Handlers ---

/// [POST /v1/data-requests]
/// Triggers the data fetching process by publishing a command to the message bus.
async fn trigger_data_fetch(
    State(state): State<AppState>,
    Json(payload): Json<DataRequest>,
) -> Result<impl IntoResponse> {
    let request_id = Uuid::new_v4();
    let command = FetchCompanyDataCommand {
        request_id,
        symbol: payload.symbol,
        market: payload.market,
    };

    info!(request_id = %request_id, "Publishing data fetch command");

    state
        .nats_client
        .publish(
            DATA_FETCH_QUEUE.to_string(),
            serde_json::to_vec(&command).unwrap().into(),
        )
        .await?;

    Ok((
        StatusCode::ACCEPTED,
        Json(RequestAcceptedResponse { request_id }),
    ))
}

/// [POST /v1/analysis-requests/:symbol]
/// Triggers the analysis report generation workflow by publishing a command.
async fn trigger_analysis_generation(
    State(state): State<AppState>,
    Path(symbol): Path<String>,
    Json(payload): Json<AnalysisRequest>,
) -> Result<impl IntoResponse> {
    let request_id = Uuid::new_v4();
    let command = GenerateReportCommand {
        request_id,
        symbol,
        template_id: payload.template_id,
    };

    info!(request_id = %request_id, "Publishing analysis generation command");

    state
        .nats_client
        .publish(
            ANALYSIS_COMMANDS_QUEUE.to_string(),
            serde_json::to_vec(&command).unwrap().into(),
        )
        .await?;

    Ok((
        StatusCode::ACCEPTED,
        Json(RequestAcceptedResponse { request_id }),
    ))
}

/// [GET /v1/companies/:symbol/profile]
/// Queries the persisted company profile from the data-persistence-service.
async fn get_company_profile(
    State(state): State<AppState>,
    Path(symbol): Path<String>,
) -> Result<impl IntoResponse> {
    let profile = state.persistence_client.get_company_profile(&symbol).await?;
    Ok(Json(profile))
}

/// [GET /v1/tasks/:request_id]
/// Aggregates task progress from all downstream provider services.
async fn get_task_progress(
    State(state): State<AppState>,
    Path(request_id): Path<Uuid>,
) -> Result<impl IntoResponse> {
    let client = reqwest::Client::new();
    let fetches = state
        .config
        .provider_services
        .iter()
        .map(|service_url| {
            let client = client.clone();
            let url = format!("{}/tasks", service_url);
            async move {
                match client.get(&url).send().await {
                    Ok(resp) => match resp.json::<Vec<TaskProgress>>().await {
                        Ok(tasks) => Some(tasks),
                        Err(e) => {
                            warn!("Failed to decode tasks from {}: {}", url, e);
                            None
                        }
                    },
                    Err(e) => {
                        warn!("Failed to fetch tasks from {}: {}", url, e);
                        None
                    }
                }
            }
        });

    let results = join_all(fetches).await;
    let mut merged: Vec<TaskProgress> = Vec::new();
    for maybe_tasks in results {
        if let Some(tasks) = maybe_tasks {
            merged.extend(tasks);
        }
    }

    if let Some(task) = merged.into_iter().find(|t| t.request_id == request_id) {
        Ok((StatusCode::OK, Json(task)).into_response())
    } else {
        Ok((StatusCode::NOT_FOUND, Json(serde_json::json!({"error": "Task not found"}))).into_response())
    }
}


// --- New Config Test Handler ---

#[derive(Deserialize, Debug)]
struct TestConfigRequest {
    r#type: String,
    #[serde(flatten)]
    data: serde_json::Value,
}

/// [POST /v1/configs/test]
/// Forwards a configuration test request to the appropriate downstream service.
async fn test_data_source_config(
    State(state): State<AppState>,
    Json(payload): Json<TestConfigRequest>,
) -> Result<impl IntoResponse> {
    info!("test_data_source_config: type={}", payload.r#type);

    let target_service_url = match payload.r#type.as_str() {
        "tushare" => state.config.provider_services.iter().find(|s| s.contains("tushare")),
        "finnhub" => state.config.provider_services.iter().find(|s| s.contains("finnhub")),
        "alphavantage" => state.config.provider_services.iter().find(|s| s.contains("alphavantage")),
        _ => {
            return Ok((
                StatusCode::BAD_REQUEST,
                Json(serde_json::json!({ "error": "Unsupported config type" })),
            ).into_response());
        }
    };

    if let Some(base_url) = target_service_url {
        let client = reqwest::Client::new();
        let target_url = format!("{}/test", base_url.trim_end_matches('/'));
        info!("Forwarding test request for '{}' to {}", payload.r#type, target_url);

        let response = client
            .post(&target_url)
            .json(&payload.data)
            .send()
            .await?;

        if !response.status().is_success() {
            let status = response.status();
            let error_text = response.text().await?;
            warn!("Downstream test for '{}' failed: status={} body={}", payload.r#type, status, error_text);
            return Ok((
                StatusCode::from_u16(status.as_u16()).unwrap_or(StatusCode::BAD_GATEWAY),
                Json(serde_json::json!({
                    "error": "Downstream service returned an error",
                    "details": error_text,
                })),
            ).into_response());
        }

        let response_json: serde_json::Value = response.json().await?;
        Ok((StatusCode::OK, Json(response_json)).into_response())
    } else {
        warn!("No downstream service found for config type: {}", payload.r#type);
        Ok((
            StatusCode::NOT_IMPLEMENTED,
            Json(serde_json::json!({ "error": "No downstream service configured for this type" })),
        ).into_response())
    }
}


// --- Config API Handlers (Proxy to data-persistence-service) ---

use common_contracts::config_models::{
    AnalysisTemplateSets, DataSourcesConfig, LlmProvidersConfig,
};

/// [GET /v1/configs/llm_providers]
async fn get_llm_providers_config(
    State(state): State<AppState>,
) -> Result<impl IntoResponse> {
    let config = state.persistence_client.get_llm_providers_config().await?;
    Ok(Json(config))
}

/// [PUT /v1/configs/llm_providers]
async fn update_llm_providers_config(
    State(state): State<AppState>,
    Json(payload): Json<LlmProvidersConfig>,
) -> Result<impl IntoResponse> {
    let updated_config = state.persistence_client.update_llm_providers_config(&payload).await?;
    Ok(Json(updated_config))
}

/// [GET /v1/configs/analysis_template_sets]
async fn get_analysis_template_sets(
    State(state): State<AppState>,
) -> Result<impl IntoResponse> {
    let config = state
        .persistence_client
        .get_analysis_template_sets()
        .await?;
    Ok(Json(config))
}

/// [PUT /v1/configs/analysis_template_sets]
async fn update_analysis_template_sets(
    State(state): State<AppState>,
    Json(payload): Json<AnalysisTemplateSets>,
) -> Result<impl IntoResponse> {
    let updated_config = state
        .persistence_client
        .update_analysis_template_sets(&payload)
        .await?;
    Ok(Json(updated_config))
}

/// [GET /v1/configs/data_sources]
async fn get_data_sources_config(
    State(state): State<AppState>,
) -> Result<impl IntoResponse> {
    let config = state.persistence_client.get_data_sources_config().await?;
    Ok(Json(config))
}

/// [PUT /v1/configs/data_sources]
async fn update_data_sources_config(
    State(state): State<AppState>,
    Json(payload): Json<DataSourcesConfig>,
) -> Result<impl IntoResponse> {
    let updated_config = state.persistence_client.update_data_sources_config(&payload).await?;
    Ok(Json(updated_config))
}

/// [GET /v1/discover-models/:provider_id]
async fn discover_models(
    State(state): State<AppState>,
    Path(provider_id): Path<String>,
) -> Result<impl IntoResponse> {
    info!("discover_models: provider_id={}", provider_id);
    let providers = state.persistence_client.get_llm_providers_config().await?;
    
    if let Some(provider) = providers.get(&provider_id) {
        let client = reqwest::Client::new();
        let url = format!("{}/models", provider.api_base_url.trim_end_matches('/'));
        info!("discover_models: target_url={} (provider_id={})", url, provider_id);
        
        let response = client
            .get(&url)
            .bearer_auth(&provider.api_key)
            .send()
            .await?;

        if !response.status().is_success() {
            let status = response.status();
            let error_text = response.text().await?;
            warn!("discover_models failed: provider_id={} status={} body={}", provider_id, status, error_text);
            // Return a structured error to the frontend
            return Ok((
                StatusCode::BAD_GATEWAY,
                Json(serde_json::json!({
                    "error": "Failed to fetch models from provider",
                    "provider_error": error_text,
                })),
            ).into_response());
        }

        let models_json: serde_json::Value = response.json().await?;
        Ok((StatusCode::OK, Json(models_json)).into_response())
    } else {
        warn!("discover_models: provider not found: {}", provider_id);
        Ok((
            StatusCode::NOT_FOUND,
            Json(serde_json::json!({ "error": "Provider not found" })),
        ).into_response())
    }
}

#[derive(Deserialize)]
struct DiscoverPreviewRequest {
    api_base_url: String,
    api_key: String,
}

/// [POST /v1/discover-models]
/// Preview discovery without persisting provider configuration.
async fn discover_models_preview(
    Json(payload): Json<DiscoverPreviewRequest>,
) -> Result<impl IntoResponse> {
    let redacted_key = if payload.api_key.is_empty() { "<empty>" } else { "<redacted>" };
    info!(
        "discover_models_preview: target_url={}/models api_key={}",
        payload.api_base_url.trim_end_matches('/'),
        redacted_key
    );
    let client = reqwest::Client::new();
    let url = format!("{}/models", payload.api_base_url.trim_end_matches('/'));
    let response = client
        .get(&url)
        .bearer_auth(&payload.api_key)
        .send()
        .await?;

    if !response.status().is_success() {
        let status = response.status();
        let error_text = response.text().await?;
        warn!("discover_models_preview failed: status={} body={}", status, error_text);
        return Ok((
            StatusCode::BAD_GATEWAY,
            Json(serde_json::json!({
                "error": "Failed to fetch models from provider",
                "provider_error": error_text,
            })),
        ).into_response());
    }

    let models_json: serde_json::Value = response.json().await?;
    Ok((StatusCode::OK, Json(models_json)).into_response())
}
