use crate::acp_runtime::AcpRuntimeManager;
use crate::document_buffer_store::BufferStore;
use crate::editor_actor::EditorRuntimeActor;
use crate::local_folder_watcher_registry::LocalFolderWatcherRegistry;
use crate::middleware::request_context::inject_request_context;
use crate::routes::build_router;
use axum::extract::Request;
use axum::middleware::Next;
use axum::response::Response;
use axum::Router;
use control_plane::{ControlPlaneStore, SqliteControlPlaneStore};
use std::env;
use std::fs;
use std::sync::Arc;
use tower_http::trace::TraceLayer;
use tracing::{error, warn};

#[derive(Debug, Clone)]
pub struct AppConfig {
    pub service_name: String,
    pub service_version: String,
    pub bind_addr: String,
    pub public_bind_addr: String,
    pub legacy_next_base_url: Option<String>,
    pub enable_legacy_next_compat: bool,
    pub enable_debug_shell_routes: bool,
    pub enable_editor_actor: 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")
                .or_else(|_| env::var("MNOTE_WEB_PUBLIC_BIND"))
                .unwrap_or_else(|_| "127.0.0.1:0".into()),
            public_bind_addr: env::var("MNOTE_WEB_PUBLIC_BIND")
                .unwrap_or_else(|_| "127.0.0.1:3000".into()),
            legacy_next_base_url: env::var("MNOTE_WEB_LEGACY_NEXT_BASE_URL")
                .ok()
                .map(|value| value.trim().trim_end_matches('/').to_string())
                .filter(|value| !value.is_empty()),
            enable_legacy_next_compat: env_bool("MNOTE_WEB_ENABLE_LEGACY_NEXT_COMPAT", false),
            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),
            enable_editor_actor: env_bool("MNOTE_WEB_ENABLE_EDITOR_ACTOR", true),
            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: None,
            convex_admin_key: None,
            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 env_bool(key: &str, default: bool) -> bool {
    env::var(key)
        .ok()
        .map(|value| matches!(value.trim(), "1" | "true" | "TRUE" | "yes" | "YES"))
        .unwrap_or(default)
}

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
}

use tokio::sync::broadcast;

#[derive(Clone)]
pub struct AppState {
    config: Arc<AppConfig>,
    local_folder_watcher_registry: LocalFolderWatcherRegistry,
    pub editor_actor: EditorRuntimeActor,
    pub block_delta_tx: broadcast::Sender<serde_json::Value>,
    pub stream_delta_tx: broadcast::Sender<serde_json::Value>,
    pub acp_runtime: Arc<AcpRuntimeManager>,
    pub buffer_store: BufferStore,
    control_plane: Arc<SqliteControlPlaneStore>,
}

impl AppState {
    pub fn new(config: AppConfig) -> Self {
        let (block_delta_tx, _) = broadcast::channel(256);
        let (stream_delta_tx, _) = broadcast::channel(256);
        let actor = EditorRuntimeActor::new();
        actor.set_block_delta_tx(block_delta_tx.clone());
        let buffer_store = BufferStore::new();
        let control_plane = Arc::new(open_control_plane_store());
        Self {
            config: Arc::new(config),
            local_folder_watcher_registry: LocalFolderWatcherRegistry::new(
                buffer_store.clone(),
                control_plane.clone(),
            ),
            editor_actor: actor,
            block_delta_tx,
            stream_delta_tx,
            acp_runtime: Arc::new(AcpRuntimeManager::from_env()),
            buffer_store,
            control_plane,
        }
    }

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

    pub fn local_folder_watcher_registry(&self) -> &LocalFolderWatcherRegistry {
        &self.local_folder_watcher_registry
    }

    pub fn control_plane(&self) -> &dyn ControlPlaneStore {
        self.control_plane.as_ref()
    }
}

#[cfg(test)]
pub(crate) fn open_control_plane_store() -> SqliteControlPlaneStore {
    SqliteControlPlaneStore::in_memory().expect("初始化测试 SQLite 控制面")
}

#[cfg(not(test))]
pub(crate) fn open_control_plane_store() -> SqliteControlPlaneStore {
    let db_path = env::var("MNOTE_CONTROL_PLANE_DB_PATH")
        .ok()
        .filter(|value| !value.trim().is_empty())
        .unwrap_or_else(|| "/mnt/Data1T/Mnote_data/control-plane/control-plane.db".to_string());
    if let Some(parent) = std::path::Path::new(&db_path).parent() {
        fs::create_dir_all(parent).expect("创建 SQLite 控制面目录");
    }
    SqliteControlPlaneStore::open(&db_path).expect("初始化 SQLite 控制面")
}

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

async fn log_failed_response(request: Request, next: Next) -> Response {
    let method = request.method().clone();
    let uri = request.uri().clone();
    let response = next.run(request).await;
    let status = response.status();
    if status.is_server_error() {
        let error_code = response
            .headers()
            .get("x-error-code")
            .and_then(|value| value.to_str().ok())
            .unwrap_or("");
        if error_code == "convex_retired" {
            warn!(
                method = %method,
                uri = %uri,
                status = %status,
                error_code = %error_code,
                "退役 legacy cloud/Convex 兼容路径被请求"
            );
        } else {
            error!(
                method = %method,
                uri = %uri,
                status = %status,
                error_code = %error_code,
                "mnote-web 请求返回服务端错误"
            );
        }
    }
    response
}

#[cfg(test)]
mod tests {
    use super::*;
    use control_plane::UpsertUserInput;

    fn test_config() -> AppConfig {
        AppConfig {
            service_name: "mnote-web".into(),
            service_version: "0.1.0".into(),
            bind_addr: "127.0.0.1:0".into(),
            public_bind_addr: "127.0.0.1:3000".into(),
            legacy_next_base_url: None,
            enable_legacy_next_compat: false,
            enable_debug_shell_routes: false,
            enable_editor_actor: true,
            hermes_base_path: "/api/hermes".into(),
            compat_next_base_path: "/api/compat/next".into(),
            convex_url: None,
            convex_admin_key: None,
            allow_dev_fixtures: false,
            query_fixtures_json: None,
            mutation_fixtures_json: None,
            dev_user_id: "dev-user".into(),
            dev_user_name: "开发用户".into(),
            dev_user_email: "dev@mnote.local".into(),
        }
    }

    #[test]
    fn app_state_initializes_sqlite_control_plane_store() {
        let state = AppState::new(test_config());

        state
            .control_plane()
            .upsert_user(UpsertUserInput {
                id: Some("shujuan".into()),
                email: Some("shujuan@163.com".into()),
                username: "shujuan".into(),
                display_name: "shujuan".into(),
                role: None,
                password_hash: None,
            })
            .expect("upsert user");

        let workspace = state
            .control_plane()
            .ensure_default_workspace("shujuan")
            .expect("default workspace");
        assert_eq!(workspace.name, "shujuan 的空间");

        let access = state
            .control_plane()
            .resolve_access("shujuan", &workspace.root_uri)
            .expect("owner access");
        assert_eq!(access.permission, "write");
    }
}
