//! Lazy project initialization.
//!
//! Defers creation of the `.furnace/` project directory, databases, agents,
//! workspace scan, and file watcher until the first MCP tool call. This
//! prevents FURNACE from polluting a user's workspace directory just because
//! their MCP client starts the server.
//!
//! # How it works
//!
//! At startup, `main()` only creates user-level dirs (`~/.furnace/`) and
//! registers placeholder tool handlers.  On the first `call_tool`, the
//! [`initialize_project`] function runs exactly once (guarded by a
//! `tokio::sync::OnceCell`), creating all project-level resources and
//! replacing placeholders with real handler implementations.

use std::sync::Arc;

use anyhow::Result;
use rmcp::service::Peer;
use rmcp::RoleServer;
use tokio::sync::{OnceCell, RwLock};

use crate::agents;
use crate::agents::elrond::ScanStats;
use crate::autoforge;
use crate::config::FurnaceConfig;
use crate::db::{KuzuGraph, LanceDb, SqliteDb};
use crate::knowledge;
use crate::llm;
use crate::mcp::handlers::*;
use crate::mcp::server::McpServer;
use crate::recipes;
use crate::scroll_export;
use crate::scroll_import;
use crate::sync as furnace_sync;
use crate::telemetry;
use crate::types;
use crate::watcher;

/// Resources that must stay alive for the duration of the server.
/// Dropping this struct stops the file watcher.
pub struct ProjectKeepAlive {
    _watcher_handle: Option<watcher::WatcherHandle>,
}

/// Initialize all project-level resources on first tool call.
///
/// Creates `.furnace/` directory, databases, agents, scans the workspace,
/// starts the file watcher, and replaces placeholder handlers in the MCP
/// server with real implementations.
///
/// This function is called exactly once via `OnceCell::get_or_try_init`.
/// If it fails, subsequent tool calls will retry.
pub async fn initialize_project(
    config: Arc<FurnaceConfig>,
    peer_cell: Arc<OnceCell<Peer<RoleServer>>>,
    server: Arc<RwLock<McpServer>>,
) -> Result<ProjectKeepAlive> {
    tracing::info!("Lazy init: creating project directories and databases");

    // -----------------------------------------------------------------------
    // 1. Create project directories
    // -----------------------------------------------------------------------
    config.ensure_project_dirs()?;

    // Generate default config.toml if this is the first run
    if config.is_first_run {
        let project_config = config.project_dir.join("config.toml");
        if let Err(e) = FurnaceConfig::write_default_config(&project_config) {
            tracing::warn!(error = %e, "Failed to write default config.toml (non-fatal)");
        } else {
            tracing::info!(path = %project_config.display(), "Default config.toml created");
        }
    }

    // -----------------------------------------------------------------------
    // 2. Initialize databases
    // -----------------------------------------------------------------------
    let sqlite = Arc::new(SqliteDb::new(&config.database.sqlite_path)?);
    let lancedb = Arc::new(LanceDb::new(&config.database.lancedb_path).await?);
    let kuzu = Arc::new(KuzuGraph::new(&config.database.kuzu_path)?);

    tracing::info!(
        project_dir = %config.project_dir.display(),
        workspace = %config.workspace_root.display(),
        first_run = config.is_first_run,
        "Databases initialized"
    );

    // -----------------------------------------------------------------------
    // 3. Startup cleanup
    // -----------------------------------------------------------------------
    match sqlite.cleanup_expired_scrolls().await {
        Ok(stats) if stats.scrolls_deleted > 0 => {
            tracing::info!(
                scrolls = stats.scrolls_deleted,
                tasks = stats.tasks_deleted,
                "Startup cleanup: expired scrolls removed"
            );
        }
        Err(e) => tracing::warn!(error = %e, "Startup cleanup failed (non-fatal)"),
        _ => {}
    }
    match sqlite.cleanup_old_search_queries(90).await {
        Ok(n) if n > 0 => tracing::info!(queries = n, "Startup cleanup: old search queries pruned"),
        Err(e) => tracing::warn!(error = %e, "Search query cleanup failed (non-fatal)"),
        _ => {}
    }

    // -----------------------------------------------------------------------
    // 4. Initialize LLM provider
    // -----------------------------------------------------------------------
    let llm_provider: Arc<dyn types::LlmProvider> = Arc::new(
        llm::SamplingLlmProvider::new(Arc::clone(&peer_cell)),
    );

    // -----------------------------------------------------------------------
    // 5. Knowledge manager + KB sources from config
    // -----------------------------------------------------------------------
    let knowledge_manager = Arc::new(
        knowledge::KnowledgeManager::new(Arc::clone(&kuzu)),
    );

    if let Some(ref kb_config) = config.knowledge {
        for src in &kb_config.sources {
            let kb_source: knowledge::KBSource = src.source
                .parse()
                .unwrap_or(knowledge::KBSource::Custom(src.source.clone()));

            knowledge_manager
                .configure_kb(knowledge::KBServerConfig {
                    source: kb_source,
                    name: src.source.clone(),
                    command: src.command.clone(),
                    args: src.args.clone(),
                    env_vars: src.env_vars.clone(),
                    collection: src.collection.clone(),
                    collection_pattern: src.collection_pattern.clone(),
                })
                .await;
        }
        tracing::info!(
            sources = kb_config.sources.len(),
            auto_search = kb_config.auto_search,
            "Knowledge base sources configured"
        );
    }

    // -----------------------------------------------------------------------
    // 6. Search orchestrator
    // -----------------------------------------------------------------------
    let search_orchestrator = Arc::new(
        crate::search::orchestrator::SearchOrchestrator::new(
            Some(Arc::clone(&lancedb)),
            Some(Arc::clone(&kuzu)),
            Arc::clone(&sqlite),
            Arc::clone(&llm_provider),
            config.search.clone(),
            config.workspace_root.clone(),
        )
        .with_kb(Arc::clone(&knowledge_manager)),
    );

    if let Err(e) = search_orchestrator.cost_optimizer().load_from_db().await {
        tracing::warn!(error = %e, "Failed to load daily spend from DB (non-fatal)");
    }

    // -----------------------------------------------------------------------
    // 7. Recipe manager
    // -----------------------------------------------------------------------
    let recipe_manager = Arc::new(
        recipes::manager::RecipeManager::new(
            config.recipes_dir.clone(),
            config.user_recipes_dir.clone(),
        )
        .await?
        .with_lancedb(Arc::clone(&lancedb)),
    );

    // -----------------------------------------------------------------------
    // 8. Initialize agents
    // -----------------------------------------------------------------------
    let gandalf = Arc::new(
        agents::gandalf::Gandalf::new(Arc::clone(&kuzu), Arc::clone(&sqlite))
            .with_search(Arc::clone(&search_orchestrator))
            .with_kb(Arc::clone(&knowledge_manager))
            .with_recipes(Arc::clone(&recipe_manager))
            .with_llm(Arc::clone(&llm_provider)),
    );
    let gimli = Arc::new(
        agents::gimli::Gimli::new(Arc::clone(&llm_provider), Arc::clone(&sqlite))
            .with_search(Arc::clone(&search_orchestrator))
            .with_lancedb(Arc::clone(&lancedb))
            .with_recipes(Arc::clone(&recipe_manager)),
    );
    let morpheus = Arc::new(
        agents::morpheus::Morpheus::new(Arc::clone(&sqlite), Arc::clone(&kuzu))
            .with_kb(Arc::clone(&knowledge_manager)),
    );
    let sync_bridge = Arc::new(
        furnace_sync::bridge::SyncBridge::new(Arc::clone(&sqlite)),
    );
    let trinity = Arc::new(
        agents::trinity::Trinity::new(Arc::clone(&llm_provider), Arc::clone(&sqlite)),
    );
    let legolas = Arc::new(agents::legolas::Legolas::with_llm(
        Arc::clone(&kuzu),
        Arc::clone(&sqlite),
        Arc::clone(&llm_provider),
    ));
    let elrond = Arc::new(
        agents::elrond::Elrond::new(
            Arc::clone(&kuzu),
            Arc::clone(&lancedb),
            config.workspace_root.clone(),
        )
        .with_llm(Arc::clone(&llm_provider)),
    );
    let aragorn = Arc::new(
        agents::aragorn::Aragorn::new(Arc::clone(&sqlite), &config.agents)
            .with_agents(Arc::clone(&gandalf), Arc::clone(&gimli))
            .with_elrond(Arc::clone(&elrond))
            .with_event_listener(
                Arc::clone(&sync_bridge) as Arc<dyn types::ScrollEventListener>,
            ),
    );
    let autoforge = Arc::new(
        autoforge::workflow::AutoForge::new(
            Arc::clone(&sqlite),
            config.workspace_root.clone(),
        )
        .with_trinity(Arc::clone(&trinity))
        .with_llm(Arc::clone(&llm_provider))
        .with_legolas(Arc::clone(&legolas))
        .with_morpheus(Arc::clone(&morpheus)),
    );

    // Scroll importers/exporters
    let scroll_exporter = Arc::new(scroll_export::ScrollExporter::new(
        config.scrolls_dir.clone(),
        Arc::clone(&sqlite),
    ));
    let scroll_importer = Arc::new(scroll_import::ScrollImporter::new(
        config.scrolls_dir.clone(),
        Arc::clone(&sqlite),
    ));

    // Telemetry (user-level: global across projects)
    let telemetry_tracker = Arc::new(
        telemetry::tracker::TelemetryTracker::new(&config.user_dir)?,
    );

    // Index all recipes into LanceDb for semantic search.
    recipe_manager.ensure_recipes_indexed().await;

    tracing::info!("All agents initialized");

    // -----------------------------------------------------------------------
    // 9. Scan workspace
    // -----------------------------------------------------------------------
    let scan_stats: Arc<tokio::sync::RwLock<ScanStats>> =
        Arc::new(tokio::sync::RwLock::new(ScanStats::default()));
    tracing::info!("Starting workspace scan...");
    match elrond.scan_workspace().await {
        Ok(stats) => {
            tracing::info!(
                files_scanned = stats.files_scanned,
                files_indexed = stats.files_indexed,
                errors = stats.errors,
                "Workspace scan complete"
            );
            *scan_stats.write().await = stats;
        }
        Err(e) => {
            tracing::warn!(error = %e, "Workspace scan failed (continuing with empty index)");
        }
    }

    // -----------------------------------------------------------------------
    // 10. First-run initialization scroll
    // -----------------------------------------------------------------------
    if config.is_first_run {
        let now = chrono::Utc::now();
        let stats = scan_stats.read().await;
        let scan_summary = if stats.files_scanned > 0 {
            format!(
                "Scanned {} files, indexed {}, {} errors",
                stats.files_scanned, stats.files_indexed, stats.errors
            )
        } else {
            "Workspace scan did not complete".to_string()
        };
        drop(stats);

        let workspace_name = config
            .workspace_root
            .file_name()
            .and_then(|n| n.to_str())
            .unwrap_or("unknown");

        let init_scroll = types::Scroll {
            id: format!("SCROLL-INIT-{}", now.timestamp_millis()),
            title: format!("Workspace initialized: {}", workspace_name),
            intent: format!(
                "FURNACE first-run initialization for {}.\n\n\
                 Project directory: {}\n\
                 {}\n\n\
                 Use `furnace_status` to see current workspace health and setup suggestions.",
                config.workspace_root.display(),
                config.project_dir.display(),
                scan_summary,
            ),
            constraints: vec![],
            tasks: vec![],
            metadata: types::ScrollMetadata {
                execution_mode: types::ExecutionMode::Forge,
                auto_forge_config: None,
                recipe_used: None,
                search_context: vec![],
            },
            status: types::ScrollStatus::Completed,
            created_at: now,
            updated_at: now,
            cleanup_after: None,
        };

        match sqlite.create_scroll(&init_scroll).await {
            Ok(()) => tracing::info!(
                scroll_id = %init_scroll.id,
                "First-run initialization scroll created"
            ),
            Err(e) => tracing::warn!(
                error = %e,
                "Failed to create initialization scroll"
            ),
        }
    }

    // -----------------------------------------------------------------------
    // 11. Start file watcher
    // -----------------------------------------------------------------------
    let watcher_handle = match watcher::start_watching(
        config.workspace_root.clone(),
        Arc::clone(&elrond),
    ) {
        Ok(handle) => {
            tracing::info!("File watcher started for incremental indexing");
            Some(handle)
        }
        Err(e) => {
            tracing::warn!(
                error = %e,
                "Failed to start file watcher (incremental indexing disabled)"
            );
            None
        }
    };

    // -----------------------------------------------------------------------
    // 12. Replace all placeholder handlers with real implementations
    // -----------------------------------------------------------------------
    {
        let mut srv = server.write().await;

        srv.replace_handler(
            "search_code",
            Arc::new(SearchCodeHandler {
                orchestrator: Arc::clone(&search_orchestrator),
                telemetry: Arc::clone(&telemetry_tracker),
            }),
        )?;

        srv.replace_handler(
            "create_scroll",
            Arc::new(CreateScrollHandler {
                sqlite: Arc::clone(&sqlite),
                gandalf: Arc::clone(&gandalf),
                telemetry: Arc::clone(&telemetry_tracker),
                recipe_manager: Arc::clone(&recipe_manager),
                scroll_exporter: Arc::clone(&scroll_exporter),
            }),
        )?;

        srv.replace_handler(
            "add_tasks",
            Arc::new(AddTasksHandler {
                sqlite: Arc::clone(&sqlite),
                scroll_exporter: Arc::clone(&scroll_exporter),
            }),
        )?;

        srv.replace_handler(
            "execute_scroll",
            Arc::new(ExecuteScrollHandler {
                aragorn: Arc::clone(&aragorn),
                autoforge: Arc::clone(&autoforge),
                sqlite: Arc::clone(&sqlite),
                telemetry: Arc::clone(&telemetry_tracker),
                scroll_exporter: Arc::clone(&scroll_exporter),
                scroll_importer: Arc::clone(&scroll_importer),
                gandalf: Arc::clone(&gandalf),
                elrond: Arc::clone(&elrond),
                scan_stats: Arc::clone(&scan_stats),
                workspace_root: config.workspace_root.clone(),
                gimli: Arc::clone(&gimli),
                morpheus: Arc::clone(&morpheus),
                trinity: Arc::clone(&trinity),
                legolas: Arc::clone(&legolas),
            }),
        )?;

        srv.replace_handler(
            "update_scroll",
            Arc::new(UpdateScrollHandler {
                sqlite: Arc::clone(&sqlite),
                sync_bridge: Arc::clone(&sync_bridge),
                scroll_exporter: Arc::clone(&scroll_exporter),
            }),
        )?;

        srv.replace_handler(
            "update_task",
            Arc::new(UpdateTaskHandler {
                sqlite: Arc::clone(&sqlite),
                sync_bridge: Arc::clone(&sync_bridge),
                scroll_exporter: Arc::clone(&scroll_exporter),
            }),
        )?;

        srv.replace_handler(
            "remove_tasks",
            Arc::new(RemoveTasksHandler {
                sqlite: Arc::clone(&sqlite),
                sync_bridge: Arc::clone(&sync_bridge),
                scroll_exporter: Arc::clone(&scroll_exporter),
            }),
        )?;

        srv.replace_handler(
            "validate_scroll",
            Arc::new(ValidateScrollHandler {
                morpheus: Arc::clone(&morpheus),
                sqlite: Arc::clone(&sqlite),
                scroll_importer: Arc::clone(&scroll_importer),
            }),
        )?;

        srv.replace_handler(
            "reload_scroll",
            Arc::new(ReloadScrollHandler {
                scroll_importer: Arc::clone(&scroll_importer),
                sqlite: Arc::clone(&sqlite),
                sync_bridge: Arc::clone(&sync_bridge),
            }),
        )?;

        srv.replace_handler(
            "list_scrolls",
            Arc::new(ListScrollsHandler {
                sqlite: Arc::clone(&sqlite),
                sync_bridge: Arc::clone(&sync_bridge),
                knowledge_manager: Some(Arc::clone(&knowledge_manager)),
            }),
        )?;

        srv.replace_handler(
            "get_scroll",
            Arc::new(GetScrollHandler {
                sqlite: Arc::clone(&sqlite),
                sync_bridge: Arc::clone(&sync_bridge),
                knowledge_manager: Some(Arc::clone(&knowledge_manager)),
            }),
        )?;

        srv.replace_handler(
            "analyze_file",
            Arc::new(AnalyzeFileHandler {
                legolas: Arc::clone(&legolas),
                sqlite: Arc::clone(&sqlite),
                workspace_root: config.workspace_root.clone(),
            }),
        )?;

        srv.replace_handler(
            "search_feedback",
            Arc::new(SearchFeedbackHandler {
                sqlite: Arc::clone(&sqlite),
                metrics: crate::search::metrics::SearchMetrics::new(Arc::clone(&sqlite)),
            }),
        )?;

        srv.replace_handler(
            "list_recipes",
            Arc::new(ListRecipesHandler {
                recipe_manager: Arc::clone(&recipe_manager),
            }),
        )?;

        srv.replace_handler(
            "render_recipe",
            Arc::new(RenderRecipeHandler {
                recipe_manager: Arc::clone(&recipe_manager),
            }),
        )?;

        srv.replace_handler(
            "configure_sync",
            Arc::new(ConfigureSyncHandler {
                sync_bridge: Arc::clone(&sync_bridge),
            }),
        )?;

        srv.replace_handler(
            "link_issue",
            Arc::new(LinkIssueHandler {
                sync_bridge: Arc::clone(&sync_bridge),
            }),
        )?;

        srv.replace_handler(
            "push_scroll",
            Arc::new(PushScrollHandler {
                sync_bridge: Arc::clone(&sync_bridge),
            }),
        )?;

        srv.replace_handler(
            "sync_status",
            Arc::new(SyncStatusHandler {
                sync_bridge: Arc::clone(&sync_bridge),
            }),
        )?;

        srv.replace_handler(
            "list_connections",
            Arc::new(ListConnectionsHandler {
                sync_bridge: Arc::clone(&sync_bridge),
            }),
        )?;

        srv.replace_handler(
            "get_metrics",
            Arc::new(GetMetricsHandler {
                telemetry: Arc::clone(&telemetry_tracker),
            }),
        )?;

        srv.replace_handler(
            "configure_kb",
            Arc::new(ConfigureKBHandler {
                knowledge_manager: Arc::clone(&knowledge_manager),
            }),
        )?;

        srv.replace_handler(
            "query_kb",
            Arc::new(QueryKBHandler {
                knowledge_manager: Arc::clone(&knowledge_manager),
            }),
        )?;

        srv.replace_handler(
            "sync_kb",
            Arc::new(SyncKBHandler {
                knowledge_manager: Arc::clone(&knowledge_manager),
            }),
        )?;

        srv.replace_handler(
            "ingest_kb",
            Arc::new(IngestKBHandler {
                knowledge_manager: Arc::clone(&knowledge_manager),
            }),
        )?;

        srv.replace_handler(
            "kb_status",
            Arc::new(KBStatusHandler {
                knowledge_manager: Arc::clone(&knowledge_manager),
            }),
        )?;

        srv.replace_handler(
            "furnace_status",
            Arc::new(FurnaceStatusHandler {
                workspace_root: config.workspace_root.clone(),
                project_dir: config.project_dir.clone(),
                user_dir: config.user_dir.clone(),
                is_first_run: config.is_first_run,
                telemetry: Arc::clone(&telemetry_tracker),
                recipe_manager: Arc::clone(&recipe_manager),
                knowledge_manager: Arc::clone(&knowledge_manager),
                sync_bridge: Arc::clone(&sync_bridge),
                sqlite: Arc::clone(&sqlite),
                scan_stats: Arc::clone(&scan_stats),
            }),
        )?;

        srv.replace_handler(
            "commit_scroll",
            Arc::new(CommitScrollHandler {
                sqlite: Arc::clone(&sqlite),
                workspace_root: config.workspace_root.clone(),
            }),
        )?;

        tracing::info!("All tool handlers wired (28 tools)");
    } // write lock released

    Ok(ProjectKeepAlive {
        _watcher_handle: watcher_handle,
    })
}
