use std::path::{Path, PathBuf};

use tracing::warn;
use vtcode_core::config::AgentClientProtocolZedWorkspaceTrustMode;
use vtcode_core::config::constants::{
    prompt_budget as prompt_budget_constants, ui as ui_constants,
};
use vtcode_core::config::core::AgentOnboardingConfig;
use vtcode_core::config::loader::VTCodeConfig;
use vtcode_core::config::types::AgentConfig as CoreAgentConfig;
use vtcode_core::project_doc::{self, ProjectDocOptions};
use vtcode_core::ui::slash::find_visible_command;
use vtcode_core::utils::common::summarize_workspace_languages;
use vtcode_terminal_detection::{TerminalFeature, TerminalType};
use vtcode_tui::app::InlineHeaderHighlight;

#[derive(Default, Clone)]
pub(crate) struct SessionBootstrap {
    pub placeholder: Option<String>,
    pub prompt_addendum: Option<String>,
    pub mcp_enabled: Option<bool>,
    pub mcp_providers: Option<Vec<vtcode_core::config::mcp::McpProviderConfig>>,
    pub mcp_error: Option<String>,
    pub search_tools_notice: Option<crate::startup::SearchToolsBundleNotice>,
    pub header_highlights: Vec<InlineHeaderHighlight>,
    pub acp_workspace_trust: Option<AgentClientProtocolZedWorkspaceTrustMode>,
}

pub(crate) async fn prepare_session_bootstrap(
    runtime_cfg: &CoreAgentConfig,
    vt_cfg: Option<&VTCodeConfig>,
    mcp_error: Option<String>,
) -> SessionBootstrap {
    let onboarding_cfg = vt_cfg
        .map(|cfg| cfg.agent.onboarding.clone())
        .unwrap_or_default();
    let todo_planning_enabled = vt_cfg
        .map(|cfg| cfg.agent.todo_planning_mode)
        .unwrap_or(true);

    let language_summary = summarize_workspace_languages(&runtime_cfg.workspace);
    let extra_instruction_files = vt_cfg
        .map(|cfg| cfg.agent.instruction_files.clone())
        .unwrap_or_default();
    let instruction_budget = vt_cfg
        .map(|cfg| cfg.agent.instruction_max_bytes)
        .unwrap_or(prompt_budget_constants::DEFAULT_MAX_BYTES);
    let project_doc_budget = vt_cfg
        .map(|cfg| cfg.agent.project_doc_max_bytes)
        .unwrap_or(prompt_budget_constants::DEFAULT_MAX_BYTES);
    let effective_budget = instruction_budget.min(project_doc_budget);

    let guideline_highlights = if onboarding_cfg.include_guideline_highlights {
        extract_guideline_highlights(
            &runtime_cfg.workspace,
            onboarding_cfg.guideline_highlight_limit,
            effective_budget,
            &extra_instruction_files,
            &vt_cfg
                .map(|cfg| cfg.agent.project_doc_fallback_filenames.clone())
                .unwrap_or_default(),
        )
        .await
    } else {
        None
    };

    let show_tooltips = vt_cfg.and_then(|cfg| cfg.tui.show_tooltips).unwrap_or(true);
    let header_highlights = if onboarding_cfg.enabled && show_tooltips {
        build_header_highlights(&onboarding_cfg)
    } else {
        Vec::new()
    };

    let prompt_addendum = if onboarding_cfg.enabled {
        build_prompt_addendum(
            &onboarding_cfg,
            language_summary.as_deref(),
            guideline_highlights.as_deref(),
            todo_planning_enabled,
        )
    } else {
        None
    };

    let placeholder = if onboarding_cfg.enabled && todo_planning_enabled {
        onboarding_cfg.chat_placeholder.as_ref().and_then(|value| {
            let trimmed = value.trim();
            if trimmed.is_empty() {
                None
            } else {
                Some(trimmed.to_string())
            }
        })
    } else {
        None
    };

    SessionBootstrap {
        placeholder,
        prompt_addendum,
        mcp_enabled: vt_cfg.map(|cfg| cfg.mcp.enabled),
        mcp_providers: vt_cfg.map(|cfg| cfg.mcp.providers.clone()),
        mcp_error,
        search_tools_notice: None,
        header_highlights,
        // Regular terminal sessions are not launched through the ACP bridge, so
        // ACP-specific workspace trust must not leak into the runtime header or
        // tool prompt policy.
        acp_workspace_trust: None,
    }
}

fn build_header_highlights(onboarding_cfg: &AgentOnboardingConfig) -> Vec<InlineHeaderHighlight> {
    let mut highlights = Vec::with_capacity(3); // Typically have 3 highlight sections

    if let Some(commands) = slash_commands_highlight() {
        highlights.push(commands);
    }

    if onboarding_cfg.include_usage_tips_in_welcome
        && let Some(usage) = usage_tips_highlight(&onboarding_cfg.usage_tips)
    {
        highlights.push(usage);
    }

    if onboarding_cfg.include_recommended_actions_in_welcome
        && let Some(actions) = recommended_actions_highlight(&onboarding_cfg.recommended_actions)
    {
        highlights.push(actions);
    }

    highlights.push(terminal_info_highlight());

    highlights
}

fn terminal_info_highlight() -> InlineHeaderHighlight {
    let (title, lines) = match TerminalType::detect() {
        Ok(term) if term != TerminalType::Unknown => {
            let mut lines = Vec::new();
            lines.push(format!("Terminal: {}", term.name()));

            let features = [
                TerminalFeature::Multiline,
                TerminalFeature::CopyPaste,
                TerminalFeature::ShellIntegration,
                TerminalFeature::ThemeSync,
            ];

            let supported: Vec<&str> = features
                .iter()
                .filter(|f| term.supports_feature(**f))
                .map(|f| match f {
                    TerminalFeature::Multiline => "Multiline",
                    TerminalFeature::CopyPaste => "Copy/Paste",
                    TerminalFeature::ShellIntegration => "Shell Integration",
                    TerminalFeature::ThemeSync => "Theme Sync",
                    TerminalFeature::Notifications => "Notifications",
                })
                .collect();

            if !supported.is_empty() {
                lines.push(format!("Capabilities: {}", supported.join(", ")));
            }

            if term.should_offer_terminal_setup() {
                lines.push("Status: Setup Required (Run /terminal-setup)".to_string());
            } else if term.has_native_multiline_support() {
                lines.push("Status: Ready (native multiline support)".to_string());
            } else {
                lines.push("Status: Manual terminal guidance available".to_string());
            }

            ("Terminal Environment".to_string(), lines)
        }
        _ => (
            "Terminal Environment".to_string(),
            vec!["Detected: Generic / Unknown".to_string()],
        ),
    };

    InlineHeaderHighlight { title, lines }
}

fn usage_tips_highlight(tips: &[String]) -> Option<InlineHeaderHighlight> {
    let entries = collect_non_empty_entries(tips);
    if entries.is_empty() {
        return None;
    }

    let lines = entries
        .into_iter()
        .map(|tip| format!("- {}", tip))
        .collect();

    Some(InlineHeaderHighlight {
        title: "Usage Tips".to_string(),
        lines,
    })
}

fn recommended_actions_highlight(actions: &[String]) -> Option<InlineHeaderHighlight> {
    let entries = collect_non_empty_entries(actions);
    if entries.is_empty() {
        return None;
    }

    let lines = entries
        .into_iter()
        .map(|action| format!("- {}", action))
        .collect();

    Some(InlineHeaderHighlight {
        title: "Suggested Next Actions".to_string(),
        lines,
    })
}

fn slash_commands_highlight() -> Option<InlineHeaderHighlight> {
    let limit = ui_constants::WELCOME_SLASH_COMMAND_LIMIT;
    if limit == 0 {
        return None;
    }

    let prefix = ui_constants::WELCOME_SLASH_COMMAND_PREFIX;
    let mut commands: Vec<(String, String)> = vec![
        (format!("{}{{command}}", prefix), String::new()),
        (
            format!("{}help", prefix),
            find_visible_command("help")
                .map(|info| info.description.trim().to_string())
                .unwrap_or_default(),
        ),
        ("Enter".to_string(), "Submit message".to_string()),
        ("Escape".to_string(), "Cancel input".to_string()),
    ];

    if limit < commands.len() {
        commands.truncate(limit);
    }

    if commands.is_empty() {
        return None;
    }

    let indent = ui_constants::WELCOME_SLASH_COMMAND_INDENT;
    let intro = ui_constants::WELCOME_SLASH_COMMAND_INTRO.trim();

    let segments: Vec<String> = commands
        .into_iter()
        .map(|(command, description)| {
            if description.is_empty() {
                command
            } else {
                format!("{command} {description}")
            }
        })
        .collect();

    if segments.is_empty() {
        return None;
    }

    let mut lines = Vec::with_capacity(10); // Estimate 10 lines for usage tips

    if !intro.is_empty() {
        lines.push(format!("{}{}", indent, intro));
    }

    for segment in segments {
        lines.push(format!("{}- {}", indent, segment));
    }

    Some(InlineHeaderHighlight {
        title: String::new(),
        lines,
    })
}

async fn extract_guideline_highlights(
    workspace: &Path,
    limit: usize,
    max_bytes: usize,
    extra_instruction_files: &[String],
    fallback_filenames: &[String],
) -> Option<Vec<String>> {
    if limit == 0 || max_bytes == 0 {
        return None;
    }

    let home_dir = determine_home_dir();
    match project_doc::read_project_doc_with_options(&ProjectDocOptions {
        current_dir: workspace,
        project_root: workspace,
        home_dir: home_dir.as_deref(),
        extra_instruction_files,
        fallback_filenames,
        exclude_patterns: &[],
        match_paths: &[],
        import_max_depth: 5,
        max_bytes,
    })
    .await
    {
        Ok(Some(bundle)) => {
            let highlights = bundle.highlights(limit);
            if highlights.is_empty() {
                None
            } else {
                Some(highlights)
            }
        }
        Ok(None) => None,
        Err(err) => {
            warn!("failed to load project documentation for highlights: {err:#}");
            None
        }
    }
}

fn determine_home_dir() -> Option<PathBuf> {
    if let Some(home) = std::env::var_os("HOME")
        && !home.is_empty()
    {
        return Some(PathBuf::from(home));
    }

    if let Some(profile) = std::env::var_os("USERPROFILE")
        && !profile.is_empty()
    {
        return Some(PathBuf::from(profile));
    }

    None
}

fn build_prompt_addendum(
    onboarding_cfg: &AgentOnboardingConfig,
    language_summary: Option<&str>,
    guideline_highlights: Option<&[String]>,
    todo_planning_enabled: bool,
) -> Option<String> {
    let mut lines = Vec::with_capacity(15); // Estimate 15 lines for prompt addendum
    lines.push("## SESSION CONTEXT".to_string());

    if onboarding_cfg.include_language_summary
        && let Some(summary) = language_summary
    {
        lines.push("### Detected Languages".to_string());
        lines.push(format!("- {}", summary));
    }

    if onboarding_cfg.include_guideline_highlights
        && let Some(highlights) = guideline_highlights
        && !highlights.is_empty()
    {
        lines.push("### Key Guidelines".to_string());
        for item in highlights.iter().take(2) {
            lines.push(format!("- {}", item));
        }
    }

    if todo_planning_enabled {
        lines.push("### Workflow Hint".to_string());
        lines.push("- Start with the goal, use `@file` or IDE context to focus, switch to Plan Mode for research/spec work, and use `task_tracker` for multi-step execution.".to_string());
    }

    push_prompt_usage_tips(&mut lines, &onboarding_cfg.usage_tips);
    push_prompt_recommended_actions(&mut lines, &onboarding_cfg.recommended_actions);

    let content = lines.join("\n");
    if content.trim() == "## SESSION CONTEXT" {
        None
    } else {
        Some(content)
    }
}

fn push_prompt_usage_tips(lines: &mut Vec<String>, tips: &[String]) {
    let entries = collect_non_empty_entries(tips);
    if entries.is_empty() {
        return;
    }

    lines.push("### Usage Tips".to_string());
    for tip in entries {
        lines.push(format!("- {}", tip));
    }
}

fn push_prompt_recommended_actions(lines: &mut Vec<String>, actions: &[String]) {
    let entries = collect_non_empty_entries(actions);
    if entries.is_empty() {
        return;
    }

    lines.push("### Suggested Next Actions".to_string());
    for action in entries {
        lines.push(format!("- {}", action));
    }
}

fn collect_non_empty_entries(items: &[String]) -> Vec<&str> {
    items
        .iter()
        .map(|item| item.trim())
        .filter(|item| !item.is_empty())
        .collect()
}
