use agent_contracts::tool::{ToolCall, ToolCallBuilder, ToolExecutor, ToolFilter, ToolSpecView};
use agent_types::common::BuildError;
use agent_types::tool::{FinalToolCall, RawToolCall};
use std::sync::Arc;

use super::ToolCallImpl;

pub struct ToolCallBuilderImpl {
    raw_tool_call: Option<RawToolCall>,
    tool_filter: Option<Box<dyn ToolFilter>>,
}

impl ToolCallBuilderImpl {
    pub fn new() -> Self {
        Self {
            raw_tool_call: None,
            tool_filter: None,
        }
    }

    fn resolve_visible_spec_by_name(
        tool_filter: &dyn ToolFilter,
        tool_name: &str,
    ) -> Option<Arc<dyn ToolSpecView>> {
        tool_filter.get_spec_for_name(tool_name)
    }

    fn resolve_visible_executor_by_name(
        tool_filter: &dyn ToolFilter,
        tool_name: &str,
    ) -> Option<Arc<dyn ToolExecutor>> {
        tool_filter.get_executor_for_name(tool_name)
    }
}

impl ToolCallBuilder for ToolCallBuilderImpl {
    fn with_raw_llm_tool_call(mut self, raw_tool_call: RawToolCall) -> Self {
        self.raw_tool_call = Some(raw_tool_call);
        self
    }

    fn with_tool_filter(mut self, tool_filter: Box<dyn ToolFilter>) -> Self {
        self.tool_filter = Some(tool_filter);
        self
    }

    fn build(self) -> Result<Box<dyn ToolCall>, BuildError> {
        let raw_tool_call = self
            .raw_tool_call
            .ok_or_else(|| BuildError::MissingRequiredField {
                field: "raw_tool_call".to_string(),
            })?;
        let tool_filter = self
            .tool_filter
            .ok_or_else(|| BuildError::MissingRequiredField {
                field: "tool_filter".to_string(),
            })?;

        let visible_spec =
            Self::resolve_visible_spec_by_name(tool_filter.as_ref(), &raw_tool_call.tool_name)
                .ok_or_else(|| BuildError::InvalidConfig {
                    message: format!(
                        "tool '{}' is not visible in the current ToolFilter",
                        raw_tool_call.tool_name
                    ),
                })?;

        let executor =
            Self::resolve_visible_executor_by_name(tool_filter.as_ref(), &raw_tool_call.tool_name)
                .ok_or_else(|| BuildError::InvalidConfig {
                    message: format!(
                        "tool '{}' is missing an executor in the current ToolFilter",
                        raw_tool_call.tool_name
                    ),
                })?;

        let _ = visible_spec.id();

        let final_call = FinalToolCall {
            call_id: raw_tool_call.call_id,
            tool_name: raw_tool_call.tool_name,
            input: raw_tool_call.input,
        };

        Ok(Box::new(ToolCallImpl::new(final_call, visible_spec, executor)) as Box<dyn ToolCall>)
    }
}