//! Docker container execution state machine.

use serde_json::Value;
use std::collections::HashMap;
use stormchaser_model::dsl::Step;

/// Cryptographic utilities for container state encryption.
pub mod crypto;
/// Docker-specific helper utilities.
pub mod docker_utils;
/// State machine transitions for containers.
pub mod transitions;

use bollard::Docker;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

/// Metadata associated with a container executing a workflow step.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ContainerMetadata {
    /// The unique identifier of the workflow run.
    pub run_id: Uuid,
    /// The unique identifier of the step being executed.
    pub step_id: Uuid,
    /// Monotonically increasing token for preventing duplicate execution.
    pub fencing_token: i64,
    /// The step specification from the workflow DSL.
    pub step_dsl: Step,
    /// When the container task was received.
    pub received_at: chrono::DateTime<chrono::Utc>,
    /// Optional encryption key for secure state sharing.
    pub encryption_key: Option<String>,
    /// Optional storage mounts configured for the container.
    pub storage: Option<HashMap<String, Value>>,
    /// Optional URLs for uploading test reports.
    pub test_report_urls: Option<HashMap<String, Value>>,
    /// Optional private registry authentication details.
    pub registry_auth: Option<Value>,
    /// Optional URL for pushing logs directly to Loki using the Docker log driver.
    pub loki_url: Option<String>,
}

/// Metrics collected during or after the container's execution.
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ContainerMetrics {
    /// The exit code of the container, if it has finished.
    pub exit_code: Option<i64>,
    /// How long the container ran in milliseconds.
    pub duration_ms: u64,
    /// How long the container took to start after being received, in milliseconds.
    pub latency_ms: u64,
    /// Hashes of the storage artifacts generated by the container.
    pub storage_hashes: Option<HashMap<String, String>>,
    /// Artifacts generated by the container.
    pub artifacts: Option<HashMap<String, Value>>,
    /// Test reports generated by the container.
    pub test_reports: Option<Value>,
}

/// The final outcome state of the container.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ContainerState {
    /// The container completed successfully.
    Succeeded(ContainerMetrics),
    /// The container failed with an error message and metrics.
    Failed(String, ContainerMetrics),
}

/// The result of attempting to start the container.
pub enum StartResult {
    /// The container was successfully started.
    Running(DockerContainerMachine<state::Running>),
    /// The container failed to start.
    #[allow(dead_code)]
    Failed(DockerContainerMachine<state::Finished>),
}

/// State types for the container state machine.
pub mod state {
    /// The initial state before starting.
    pub struct Initialized;
    /// The state when the container is currently running.
    pub struct Running {
        /// The name of the Docker container.
        pub container_name: String,
        /// The time the container was dispatched to run.
        pub dispatched_at: chrono::DateTime<chrono::Utc>,
        /// Docker volumes that need to be cleaned up after execution.
        pub volumes_to_cleanup: Vec<String>,
        /// Names of the storage endpoints.
        pub storage_names: Vec<String>,
        /// The mounts configured for this container.
        pub mounts: Vec<bollard::service::Mount>,
    }
    /// The state when the container has finished executing.
    pub struct Finished {
        /// The final result of the execution.
        pub result: super::ContainerState,
    }
}

/// The core state machine representing a Docker container's lifecycle.
#[derive(Clone)]
pub struct DockerContainerMachine<S> {
    /// The NATS client used for messaging, if available.
    pub nats: Option<async_nats::Client>,
    /// The Docker client.
    pub docker: Docker,
    /// Metadata associated with this container execution.
    pub metadata: ContainerMetadata,
    /// The current state of the machine.
    pub state: S,
}

impl DockerContainerMachine<state::Initialized> {
    /// Creates a new, initialized container state machine.
    pub fn new(
        docker: Docker,
        metadata: ContainerMetadata,
        nats: Option<async_nats::Client>,
    ) -> Self {
        Self {
            nats,
            docker,
            metadata,
            state: state::Initialized,
        }
    }
}
