//! Control-plane reply and error contracts.

use std::collections::BTreeSet;
use std::path::PathBuf;
use std::time::SystemTime;

use crate::command::DeploymentPermissionMode;
use model_core::binary_identity::BinaryIdentity;
use model_core::capability::Capability;
use model_core::ids::{ProfileName, TraceId, TraceName};
use model_core::process::NamespaceIdentity;
use model_core::trace::{TraceHealth, TraceLifecycleState};
use plugin_system::PluginInstanceStatus;

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TraceListItem {
    pub trace_id: TraceId,
    pub display_name: TraceName,
    pub root_pid: u32,
    /// Kernel PID namespace captured for the trace root. This query field is
    /// independent of runtime container metadata; authorization additionally
    /// checks the peer's mount namespace.
    pub root_pid_namespace: Option<NamespaceIdentity>,
    /// Runtime container identity resolved by the daemon from the root
    /// process cgroup. `None` means the trace is host-rooted or the runtime
    /// layout did not yield an identity.
    pub root_container_id: Option<String>,
    pub lifecycle_state: TraceLifecycleState,
    pub health: TraceHealth,
    pub tags: BTreeSet<String>,
    pub created_at: SystemTime,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct TrackAddReply {
    pub trace_id: TraceId,
    pub lifecycle_state: TraceLifecycleState,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LaunchPermissionsReply {
    pub requested_host_ebpf: DeploymentPermissionMode,
    pub requested_seccomp_notify: DeploymentPermissionMode,
    pub selected_host_ebpf: bool,
    pub selected_seccomp_notify: bool,
    pub selected_profile_name: ProfileName,
    pub payload_tls_seccomp: bool,
    pub payload_socket_seccomp: bool,
    pub process_seccomp: bool,
    pub network_control_seccomp: bool,
    pub command_control_seccomp: bool,
    pub file_mkdir_seccomp: bool,
    pub file_rmdir_seccomp: bool,
    pub required_capabilities: Vec<Capability>,
    pub degraded: bool,
    pub reasons: Vec<String>,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LaunchTlsPlanDescriptor {
    pub target: PathBuf,
    pub target_identity: BinaryIdentity,
    pub binary: PathBuf,
    pub binary_identity: BinaryIdentity,
    pub provider: String,
    pub source: String,
    pub points: String,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum LaunchTlsPlanStatus {
    Found(Vec<LaunchTlsPlanDescriptor>),
    Unsupported { reason: String },
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct LaunchTlsPlanReply {
    pub status: LaunchTlsPlanStatus,
    pub cache_hit: bool,
    pub resolve_elapsed_micros: u64,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct DoctorReply {
    pub available_collectors: Vec<String>,
    pub loaded_policy_plugins: Vec<String>,
    pub storage_ready: bool,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PluginCommandReply {
    pub instance_id: String,
    pub exit_code: i32,
    pub stdout: String,
    pub stderr: String,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PluginConfigReply {
    pub instance_id: String,
    pub plugin_id: String,
    pub editable: bool,
    pub config_json: String,
    pub schema_json: String,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct PluginConfigValidationReply {
    pub instance_id: String,
    pub valid: bool,
    pub errors: Vec<String>,
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub enum ControlReply {
    LaunchPermissions(LaunchPermissionsReply),
    LaunchTlsPlan(LaunchTlsPlanReply),
    TrackAdded(TrackAddReply),
    SeccompListenerRegistered,
    TrackRemoved,
    TraceList(Vec<TraceListItem>),
    Doctor(DoctorReply),
    PluginList(Vec<PluginInstanceStatus>),
    PluginStatus(PluginInstanceStatus),
    PluginCommand(PluginCommandReply),
    PluginConfig(PluginConfigReply),
    PluginConfigValidation(PluginConfigValidationReply),
}

#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ControlError {
    pub code: String,
    pub message: String,
}

impl ControlError {
    pub fn new(code: impl Into<String>, message: impl Into<String>) -> Self {
        Self {
            code: code.into(),
            message: message.into(),
        }
    }
}