use crate::middleware::request_context::inject_request_context;
use crate::routes::build_router;
use axum::Router;
use std::env;
use std::fs;
use std::sync::Arc;
use tower_http::trace::TraceLayer;

#[derive(Debug, Clone)]
pub struct AppConfig {
    pub service_name: String,
    pub service_version: String,
    pub bind_addr: String,
    pub enable_debug_shell_routes: bool,
    pub hermes_base_path: String,
    pub compat_next_base_path: String,
    pub convex_url: Option<String>,
    pub convex_admin_key: Option<String>,
    pub allow_dev_fixtures: bool,
    pub query_fixtures_json: Option<String>,
    pub mutation_fixtures_json: Option<String>,
    pub dev_user_id: String,
    pub dev_user_name: String,
    pub dev_user_email: String,
}

impl AppConfig {
    pub fn from_env() -> Self {
        Self {
            service_name: env::var("MNOTE_WEB_SERVICE_NAME").unwrap_or_else(|_| "mnote-web".into()),
            service_version: env::var("MNOTE_WEB_SERVICE_VERSION")
                .unwrap_or_else(|_| env!("CARGO_PKG_VERSION").into()),
            bind_addr: env::var("MNOTE_WEB_BIND").unwrap_or_else(|_| "127.0.0.1:0".into()),
            enable_debug_shell_routes: env::var("MNOTE_WEB_ENABLE_DEBUG_SHELL_ROUTES")
                .ok()
                .map(|value| matches!(value.trim(), "1" | "true" | "TRUE" | "yes" | "YES"))
                .unwrap_or(false),
            hermes_base_path: env::var("MNOTE_WEB_HERMES_BASE_PATH")
                .unwrap_or_else(|_| "/api/hermes".into()),
            compat_next_base_path: env::var("MNOTE_WEB_COMPAT_NEXT_BASE_PATH")
                .unwrap_or_else(|_| "/api/compat/next".into()),
            convex_url: env::var("CONVEX_SELF_HOSTED_URL")
                .ok()
                .or_else(|| env::var("NEXT_PUBLIC_CONVEX_URL").ok())
                .or_else(|| read_env_or_dotenv("CONVEX_SELF_HOSTED_URL"))
                .or_else(|| read_env_or_dotenv("NEXT_PUBLIC_CONVEX_URL"))
                .map(|value| value.trim().trim_end_matches('/').to_string())
                .filter(|value| !value.is_empty()),
            convex_admin_key: env::var("CONVEX_SELF_HOSTED_ADMIN_KEY")
                .ok()
                .or_else(|| read_env_or_dotenv("CONVEX_SELF_HOSTED_ADMIN_KEY"))
                .map(|value| value.trim().to_string())
                .filter(|value| !value.is_empty()),
            allow_dev_fixtures: env::var("MNOTE_WEB_ALLOW_DEV_FIXTURES")
                .ok()
                .map(|value| matches!(value.trim(), "1" | "true" | "TRUE" | "yes" | "YES"))
                .unwrap_or(false),
            query_fixtures_json: env::var("MNOTE_WEB_QUERY_FIXTURES_JSON")
                .ok()
                .map(|value| value.trim().to_string())
                .filter(|value| !value.is_empty()),
            mutation_fixtures_json: env::var("MNOTE_WEB_MUTATION_FIXTURES_JSON")
                .ok()
                .map(|value| value.trim().to_string())
                .filter(|value| !value.is_empty()),
            dev_user_id: env::var("DEV_USER_ID")
                .ok()
                .or_else(|| read_env_or_dotenv("DEV_USER_ID"))
                .unwrap_or_else(|| "dev-user".into()),
            dev_user_name: env::var("DEV_USER_NAME")
                .ok()
                .or_else(|| read_env_or_dotenv("DEV_USER_NAME"))
                .unwrap_or_else(|| "开发用户".into()),
            dev_user_email: env::var("DEV_USER_EMAIL")
                .ok()
                .or_else(|| read_env_or_dotenv("DEV_USER_EMAIL"))
                .unwrap_or_else(|| "dev@mnote.local".into()),
        }
    }
}

fn read_env_or_dotenv(key: &str) -> Option<String> {
    if let Ok(value) = env::var(key) {
        let trimmed = value.trim().trim_matches('"').to_string();
        if !trimmed.is_empty() {
            return Some(trimmed);
        }
    }

    let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("../../..")
        .join(".env.all");
    let content = fs::read_to_string(root).ok()?;
    for line in content.lines() {
        let line = line.trim_end_matches('\r');
        if line.starts_with('#') || line.trim().is_empty() {
            continue;
        }
        let Some((k, v)) = line.split_once('=') else {
            continue;
        };
        if k.trim() != key {
            continue;
        }
        let trimmed = v.trim().trim_matches('"').to_string();
        if !trimmed.is_empty() {
            return Some(trimmed);
        }
    }
    None
}

#[derive(Debug, Clone)]
pub struct AppState {
    config: Arc<AppConfig>,
}

impl AppState {
    pub fn new(config: AppConfig) -> Self {
        Self {
            config: Arc::new(config),
        }
    }

    pub fn config(&self) -> &AppConfig {
        self.config.as_ref()
    }
}

pub fn build_app(state: AppState) -> Router {
    build_router(state)
        .layer(TraceLayer::new_for_http())
        .layer(axum::middleware::from_fn(inject_request_context))
}
