use agent_orchestrator::config_load::now_ts;
use agent_orchestrator::health::{
    increment_consecutive_errors, mark_agent_diseased, reset_consecutive_errors,
    update_capability_health,
};
use agent_orchestrator::metrics::MetricsCollector;
use agent_orchestrator::state::InnerState;
use agent_orchestrator::task_repository::NewCommandRun;
use anyhow::Result;
use serde_json::json;
use std::path::Path;
use std::sync::Arc;

use super::types::{PhaseSetup, ValidatedOutput};

/// Stage 5: Construct results, insert events, update metrics.
#[allow(clippy::too_many_arguments)]
pub(super) async fn record_phase_results(
    state: &Arc<InnerState>,
    setup: &PhaseSetup,
    validated: &ValidatedOutput,
    session_id: &Option<String>,
    task_id: &str,
    item_id: &str,
    step_id: &str,
    phase: &str,
    step_scope: agent_orchestrator::config::StepScope,
    tty: bool,
    workspace_root: &Path,
    workspace_id: &str,
    agent_id: &str,
    duration: std::time::Duration,
) -> Result<()> {
    let writer = state.db_writer.clone();
    let task_id_owned = task_id.to_string();
    let item_id_owned = item_id.to_string();
    let insert_payload = NewCommandRun {
        id: setup.run_id.clone(),
        task_item_id: item_id.to_string(),
        phase: phase.to_string(),
        command: setup.command.clone(),
        command_template: setup.command_template.clone(),
        cwd: workspace_root.to_string_lossy().to_string(),
        workspace_id: workspace_id.to_string(),
        agent_id: agent_id.to_string(),
        exit_code: validated.final_exit_code,
        stdout_path: setup.stdout_path.to_string_lossy().to_string(),
        stderr_path: setup.stderr_path.to_string_lossy().to_string(),
        started_at: setup.now.clone(),
        ended_at: now_ts(),
        interrupted: 0,
        output_json: serde_json::to_string(&validated.redacted_output)?,
        artifacts_json: serde_json::to_string(&validated.redacted_output.artifacts)?,
        confidence: Some(validated.redacted_output.confidence),
        quality_score: Some(validated.redacted_output.quality_score),
        validation_status: validated.validation_status.to_string(),
        session_id: session_id.clone(),
        machine_output_source: if tty {
            "output_json_path".to_string()
        } else {
            "stdout".to_string()
        },
        output_json_path: session_id
            .as_ref()
            .map(|sid| {
                state
                    .logs_dir
                    .join("sessions")
                    .join(sid)
                    .join("output.json")
            })
            .map(|p| p.to_string_lossy().to_string()),
        command_rule_index: None,
    };

    let validation_event_payload_json = validated.validation_event_payload_json.clone();
    {
        let mut events = Vec::with_capacity(2);
        if let Some(payload_json) = validation_event_payload_json {
            events.push(agent_orchestrator::db_write::DbEventRecord {
                task_id: task_id_owned.clone(),
                task_item_id: Some(item_id_owned.clone()),
                event_type: "output_validation_failed".to_string(),
                payload_json,
            });
        }
        if validated.sandbox_denied {
            let event_type = validated.sandbox_event_type.unwrap_or("sandbox_denied");
            events.push(agent_orchestrator::db_write::DbEventRecord {
                task_id: task_id_owned.clone(),
                task_item_id: Some(item_id_owned.clone()),
                event_type: event_type.to_string(),
                payload_json: serde_json::to_string(&json!({
                    "step": phase,
                    "step_id": step_id,
                    "step_scope": match step_scope {
                        agent_orchestrator::config::StepScope::Task => "task",
                        agent_orchestrator::config::StepScope::Item => "item",
                    },
                    "agent_id": agent_id,
                    "run_id": setup.run_id,
                    "execution_profile": setup.execution_profile.name,
                    "execution_mode": match setup.execution_profile.mode {
                        agent_orchestrator::config::ExecutionProfileMode::Host => "host",
                        agent_orchestrator::config::ExecutionProfileMode::Sandbox => "sandbox",
                    },
                    "reason_code": validated.sandbox_reason_code,
                    "reason": validated.sandbox_denial_reason,
                    "resource_kind": validated
                        .sandbox_resource_kind
                        .as_ref()
                        .map(|value| value.as_str()),
                    "network_target": validated.sandbox_network_target,
                    "stderr_excerpt": validated.sandbox_denial_stderr_excerpt,
                    "backend": agent_orchestrator::runner::sandbox_backend_label(&setup.execution_profile),
                }))?,
            });
        }
        writer
            .update_command_run_with_owned_events(insert_payload, events)
            .await?;
    }

    update_capability_health(state, agent_id, Some(phase), validated.success).await;

    let duration_ms = duration.as_millis() as u64;
    {
        let mut metrics_map = state.agent_metrics.write().await;
        let metrics = metrics_map
            .entry(agent_id.to_string())
            .or_insert_with(MetricsCollector::new_agent_metrics);
        if validated.success {
            MetricsCollector::record_success(metrics, duration_ms);
        } else {
            MetricsCollector::record_failure(metrics);
        }
        MetricsCollector::decrement_load(metrics);
    }

    agent_orchestrator::agent_lifecycle::decrement_in_flight_and_check(state, agent_id).await;

    // Agent infrastructure failure — the agent itself could not function.
    // Distinct from "task conclusion is negative" (exit_code > 0) where
    // the agent completed its work correctly.
    let agent_infra_failed = validated.final_exit_code < 0
        || validated.sandbox_denied
        || validated.validation_status == "failed";

    if agent_infra_failed {
        // Resolve the agent's health policy: agent-level > workspace-level > global default.
        let health_policy = {
            let cfg_snap = state.config_runtime.load();
            let project = cfg_snap
                .active_config
                .config
                .projects
                .values()
                .find(|p| p.agents.contains_key(agent_id));
            let agent_policy = project
                .and_then(|p| p.agents.get(agent_id))
                .map(|a| &a.health_policy);
            if agent_policy.is_none_or(|p| p.is_default()) {
                // Agent has no explicit override — try workspace fallback.
                let ws_policy = project
                    .and_then(|p| p.workspaces.get(workspace_id))
                    .map(|ws| &ws.health_policy);
                ws_policy.cloned().unwrap_or_default()
            } else {
                agent_policy.cloned().unwrap_or_default()
            }
        };

        if health_policy.disease_duration_hours > 0 {
            let errors = increment_consecutive_errors(state, agent_id).await;
            if errors >= health_policy.disease_threshold {
                mark_agent_diseased(state, agent_id, &health_policy).await;
            }
        }
    } else {
        reset_consecutive_errors(state, agent_id).await;
    }
    if let Some(sid) = session_id.as_deref() {
        let _ = state
            .session_store
            .update_session_state(sid, "closed", Some(validated.final_exit_code), true)
            .await;
    }

    Ok(())
}
