use agent_contracts::runtime::RuntimeView;
use agent_types::events::ToolLifecycleEvent;
use agent_types::tool::{
    FinalToolCall, ToolExecutionError, ToolExecutionResult, ToolExecutorOutput,
    ToolLifecycleRecord, ToolLifecycleStatus,
};
use std::time::{SystemTime, UNIX_EPOCH};

use super::state::{ExecutorPhaseResult, ToolExecutionState};
use super::ToolCallImpl;

impl ToolCallImpl {
    pub(super) fn load_execution_context(
        &self,
        final_call: &FinalToolCall,
        runtime: &dyn RuntimeView,
    ) -> Result<ToolLifecycleRecord, ToolExecutionError> {
        Ok(runtime.state_store().begin(final_call, self.spec.as_ref()))
    }

    pub(super) fn mark_lifecycle_running(
        &self,
        state: &mut ToolExecutionState,
        runtime: &dyn RuntimeView,
    ) {
        if let Some(record) = state.lifecycle_record.as_mut() {
            record.status = ToolLifecycleStatus::Running;
            runtime.state_store().update(record);
        }
    }

    pub(super) fn persist_terminal_lifecycle(
        &self,
        state: &mut ToolExecutionState,
        result: &ToolExecutionResult,
        runtime: &dyn RuntimeView,
    ) {
        let Some(record) = state.lifecycle_record.as_mut() else {
            return;
        };

        record.finished_at_ms = Some(current_time_ms());

        match result {
            ToolExecutionResult::Completed { .. } => {
                record.status = ToolLifecycleStatus::Completed;
                runtime.state_store().finish(record, result);
            }
            ToolExecutionResult::Suspended { .. } => {
                record.status = ToolLifecycleStatus::Suspended;
                runtime.state_store().finish(record, result);
            }
            ToolExecutionResult::Denied { .. } => {
                record.status = ToolLifecycleStatus::Denied;
                runtime.state_store().finish(record, result);
            }
            ToolExecutionResult::Failed {
                execution_error, ..
            } => {
                record.status = ToolLifecycleStatus::Failed;
                runtime.state_store().fail(record, execution_error);
            }
        }
    }

    pub(super) async fn invoke_resolved_executor(
        &self,
        final_call: &FinalToolCall,
        runtime: &dyn RuntimeView,
    ) -> Result<ExecutorPhaseResult, ToolExecutionError> {
        match crate::scope_tool_invocation(final_call.tool_name.clone(), async {
            self.executor.invoke(final_call, runtime).await
        })
        .await
        {
            Ok(ToolExecutorOutput::Completed { raw_outcome }) => {
                Ok(ExecutorPhaseResult::Completed(raw_outcome))
            }
            Ok(ToolExecutorOutput::Suspended { suspend_token }) => {
                Ok(ExecutorPhaseResult::Suspended(suspend_token))
            }
            Err(execution_error) => Ok(ExecutorPhaseResult::Failed(execution_error)),
        }
    }

    pub(super) fn emit_tool_event(&self, runtime: &dyn RuntimeView, event: ToolLifecycleEvent) {
        runtime.tool_events().emit(event);
    }
}

fn current_time_ms() -> u64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .expect("system clock before UNIX epoch")
        .as_millis() as u64
}