use super::{
    ToolFormatter,
    helpers::{separator_line, tool_error_user_message, truncate_lines},
};
use crate::tui::theme::Theme;
use ratatui::{
    style::Style,
    text::{Line, Span},
};
use serde_json::Value;
use steer_grpc::client_api::{ToolResult, default_agent_spec_id};
use steer_tools::tools::dispatch_agent::{
    DispatchAgentParams, DispatchAgentTarget, WorkspaceTarget,
};

pub struct DispatchAgentFormatter;

impl ToolFormatter for DispatchAgentFormatter {
    fn compact(
        &self,
        params: &Value,
        result: &Option<ToolResult>,
        _wrap_width: usize,
        theme: &Theme,
    ) -> Vec<Line<'static>> {
        let mut lines = Vec::new();

        let Ok(params) = serde_json::from_value::<DispatchAgentParams>(params.clone()) else {
            return vec![Line::from(Span::styled(
                "Invalid agent params",
                theme.error_text(),
            ))];
        };

        let preview = if params.prompt.len() > 60 {
            format!("{}...", &params.prompt[..57])
        } else {
            params.prompt.clone()
        };

        let (agent_id, workspace_summary, params_session_id) = match &params.target {
            DispatchAgentTarget::New { workspace, agent } => {
                let agent_id = agent
                    .as_deref()
                    .filter(|value| !value.trim().is_empty())
                    .map_or_else(|| default_agent_spec_id().to_string(), str::to_string);
                let workspace_summary = match workspace {
                    WorkspaceTarget::Current => "current".to_string(),
                    WorkspaceTarget::New { name } => format!("{name} (new)"),
                };
                (agent_id, workspace_summary, None)
            }
            DispatchAgentTarget::Resume { session_id } => (
                "resume".to_string(),
                "session".to_string(),
                Some(session_id.clone()),
            ),
        };

        let session_label = match result {
            Some(ToolResult::Agent(agent_result)) => agent_result.session_id.clone(),
            _ => None,
        }
        .or(params_session_id);

        let info = match result {
            Some(ToolResult::Agent(agent_result)) => {
                let line_count = agent_result.content.lines().count();
                format!("{line_count} lines")
            }
            Some(ToolResult::Error(_)) => "failed".to_string(),
            Some(_) => "unexpected result type".to_string(),
            None => "running...".to_string(),
        };

        lines.push(Line::from(vec![
            Span::styled(format!("agent={agent_id} "), theme.subtle_text()),
            Span::styled(
                format!("workspace={workspace_summary} "),
                theme.subtle_text(),
            ),
            session_label.as_ref().map_or_else(
                || Span::raw(""),
                |id| Span::styled(format!("session={id} "), theme.subtle_text()),
            ),
            Span::styled(format!("task='{preview}' "), Style::default()),
            Span::styled(format!("({info})"), theme.subtle_text()),
        ]));

        lines
    }

    fn detailed(
        &self,
        params: &Value,
        result: &Option<ToolResult>,
        wrap_width: usize,
        theme: &Theme,
    ) -> Vec<Line<'static>> {
        let mut lines = Vec::new();

        let Ok(params) = serde_json::from_value::<DispatchAgentParams>(params.clone()) else {
            return vec![Line::from(Span::styled(
                "Invalid agent params",
                theme.error_text(),
            ))];
        };

        let (agent_id, workspace_label, params_session_id) = match &params.target {
            DispatchAgentTarget::New { workspace, agent } => {
                let agent_id = agent
                    .as_deref()
                    .filter(|value| !value.trim().is_empty())
                    .map_or_else(|| default_agent_spec_id().to_string(), str::to_string);
                let workspace_label = match workspace {
                    WorkspaceTarget::Current => "current".to_string(),
                    WorkspaceTarget::New { name } => format!("{name} (new)"),
                };
                (agent_id, workspace_label, None)
            }
            DispatchAgentTarget::Resume { session_id } => (
                "resume".to_string(),
                "session".to_string(),
                Some(session_id.clone()),
            ),
        };

        lines.push(Line::from(vec![
            Span::styled("Agent: ", theme.subtle_text()),
            Span::styled(agent_id, Style::default()),
        ]));
        lines.push(Line::from(vec![
            Span::styled("Workspace: ", theme.subtle_text()),
            Span::styled(workspace_label, Style::default()),
        ]));
        if let Some(session_id) = params_session_id.as_ref() {
            lines.push(Line::from(vec![
                Span::styled("Session: ", theme.subtle_text()),
                Span::styled(session_id.clone(), Style::default()),
            ]));
        }

        lines.push(Line::from(Span::styled("Instructions:", theme.text())));
        for line in params.prompt.lines() {
            for wrapped_line in textwrap::wrap(line, wrap_width) {
                lines.push(Line::from(Span::styled(
                    wrapped_line.to_string(),
                    Style::default(),
                )));
            }
        }

        // Show output if we have results
        if let Some(result) = result {
            match result {
                ToolResult::Agent(agent_result) => {
                    if let Some(session_id) = agent_result.session_id.as_ref() {
                        lines.push(separator_line(wrap_width, theme.dim_text()));
                        lines.push(Line::from(Span::styled("Session:", theme.subtle_text())));
                        lines.push(Line::from(Span::styled(
                            format!("  {session_id}"),
                            Style::default(),
                        )));
                    }

                    if let Some(workspace) = agent_result.workspace.as_ref() {
                        lines.push(separator_line(wrap_width, theme.dim_text()));
                        lines.push(Line::from(Span::styled(
                            "Workspace Revision:",
                            theme.subtle_text(),
                        )));
                        if let Some(workspace_id) = workspace.workspace_id.as_ref() {
                            lines.push(Line::from(Span::styled(
                                format!("  id: {workspace_id}"),
                                Style::default(),
                            )));
                        }
                        if let Some(revision) = workspace.revision.as_ref() {
                            let kind = revision.vcs_kind.as_str();
                            let mut summary_line =
                                format!("  {}: revision {}", kind, revision.revision_id);
                            if let Some(change_id) = revision.change_id.as_ref() {
                                summary_line.push_str(&format!("  change {change_id}"));
                            }
                            lines.push(Line::from(Span::styled(summary_line, Style::default())));
                            if !revision.summary.trim().is_empty() {
                                lines.push(Line::from(Span::styled(
                                    format!("  summary: {}", revision.summary),
                                    Style::default(),
                                )));
                            }
                        }
                    }

                    if !agent_result.content.trim().is_empty() {
                        lines.push(separator_line(wrap_width, theme.dim_text()));

                        const MAX_OUTPUT_LINES: usize = 30;
                        let (output_lines, truncated) =
                            truncate_lines(&agent_result.content, MAX_OUTPUT_LINES);

                        for line in output_lines {
                            for wrapped in textwrap::wrap(line, wrap_width) {
                                lines.push(Line::from(Span::raw(wrapped.to_string())));
                            }
                        }

                        if truncated {
                            lines.push(Line::from(Span::styled(
                                format!(
                                    "... ({} more lines)",
                                    agent_result.content.lines().count() - MAX_OUTPUT_LINES
                                ),
                                theme.subtle_text(),
                            )));
                        }
                    }
                }
                ToolResult::Error(error) => {
                    lines.push(separator_line(wrap_width, theme.dim_text()));
                    lines.push(Line::from(Span::styled(
                        tool_error_user_message(error).into_owned(),
                        theme.error_text(),
                    )));
                }
                _ => {
                    lines.push(Line::from(Span::styled(
                        "Unexpected result type",
                        theme.error_text(),
                    )));
                }
            }
        }

        lines
    }

    fn approval(&self, params: &Value, wrap_width: usize, theme: &Theme) -> Vec<Line<'static>> {
        let mut lines = Vec::new();

        let Ok(params) = serde_json::from_value::<DispatchAgentParams>(params.clone()) else {
            return vec![Line::from(Span::styled(
                "Invalid agent params",
                theme.error_text(),
            ))];
        };

        let (agent_id, workspace_label, params_session_id) = match &params.target {
            DispatchAgentTarget::New { workspace, agent } => {
                let agent_id = agent
                    .as_deref()
                    .filter(|value| !value.trim().is_empty())
                    .map_or_else(|| default_agent_spec_id().to_string(), str::to_string);
                let workspace_label = match workspace {
                    WorkspaceTarget::Current => "current".to_string(),
                    WorkspaceTarget::New { name } => format!("{name} (new)"),
                };
                (agent_id, workspace_label, None)
            }
            DispatchAgentTarget::Resume { session_id } => (
                "resume".to_string(),
                "session".to_string(),
                Some(session_id.clone()),
            ),
        };

        lines.push(Line::from(vec![
            Span::styled("Agent: ", theme.subtle_text()),
            Span::styled(agent_id, Style::default()),
        ]));
        lines.push(Line::from(vec![
            Span::styled("Workspace: ", theme.subtle_text()),
            Span::styled(workspace_label, Style::default()),
        ]));
        if let Some(session_id) = params_session_id.as_ref() {
            lines.push(Line::from(vec![
                Span::styled("Session: ", theme.subtle_text()),
                Span::styled(session_id.clone(), Style::default()),
            ]));
        }

        lines.push(Line::from(Span::styled(
            "Instructions:",
            theme.subtle_text(),
        )));
        for line in params.prompt.lines() {
            for wrapped_line in textwrap::wrap(line, wrap_width) {
                lines.push(Line::from(Span::styled(
                    wrapped_line.to_string(),
                    Style::default(),
                )));
            }
        }

        lines
    }
}
