use secrecy::SecretString;
use thiserror::Error;
use tokio::task::JoinHandle;

use crate::announce::{AnnounceError, send_announce};
use crate::ext::OrchestratorInitExt;
use crate::handshake::{InitError, read_init};
use crate::ipc::process_ipc_messages;
use crate::logging::init_module;
use crate::secrets::{ModuleSecretError, get_module_secret_client, get_secret};
use crate::state::AppState;
use crate::{AnnouncedEndpoint, ModuleAnnounce, OrchestratorInit};

/// Errors that may occur during bootstrap initialization.
#[derive(Debug, Error)]
pub enum BootstrapError {
    #[error("handshake failed: {0}")]
    Init(#[from] InitError),

    #[error("secret client error: {0}")]
    Secret(#[from] ModuleSecretError),

    #[error("announcement error: {0}")]
    Announce(#[from] AnnounceError),

    #[error("other error: {0}")]
    Other(String),
}

/// Bootstraps a PyWatt module with handshake, secret init, announcement, and IPC.
///
/// - `secret_keys`: list of environment secret names to fetch initially.
/// - `endpoints`: list of HTTP/WebSocket endpoints for announcement.
/// - `state_builder`: callback to build module-specific state from the orchestrator init and fetched secrets.
///
/// Returns a tuple of `(AppState<T>, JoinHandle<()>)` where the join handle is a spawned task running the IPC loop.
pub async fn bootstrap_module<T, F>(
    secret_keys: Vec<String>,
    endpoints: Vec<AnnouncedEndpoint>,
    state_builder: F,
) -> Result<(AppState<T>, JoinHandle<()>), BootstrapError>
where
    F: Fn(&OrchestratorInit, Vec<SecretString>) -> T + Send + Sync + 'static,
{
    // 1. Initialize logging and redaction
    init_module();

    // 2. Handshake: read orchestrator init
    let init: OrchestratorInit = read_init().await?;

    // 3. Secret client
    let client = get_module_secret_client(&init.orchestrator_api, &init.module_id).await?;

    // 4. Fetch initial secrets
    let mut secrets = Vec::new();
    for key in &secret_keys {
        let s = get_secret(&client, key).await?;
        secrets.push(s.clone());
    }

    // 5. Build application state
    let user_state = state_builder(&init, secrets);
    let app_state = AppState::new(
        init.module_id.clone(),
        init.orchestrator_api.clone(),
        client.clone(),
        user_state,
    );

    // 6. Send announcement
    let listen_str = init.listen_to_string();
    let announce = ModuleAnnounce {
        listen: listen_str,
        endpoints,
    };
    send_announce(&announce)?;

    // 7. Spawn IPC processing loop
    let ipc_handle = tokio::spawn(process_ipc_messages());

    Ok((app_state, ipc_handle))
}
