use serde::{Deserialize, Serialize, de::DeserializeOwned};

use crate::Specification;

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(bound(serialize = "T: Serialize", deserialize = "T: DeserializeOwned"))]
pub struct ManifestState<T>
where
    T: Specification,
{
    pub manifest: Manifest<T>,
    pub manifest_hash: Vec<u8>,
    pub generation: u64,
    pub status: ManifestStatus,

    pub created: jiff::Timestamp,
    pub updated: jiff::Timestamp,

    pub lease: Option<ManifestLease>,
}

pub type WorkerId = uuid::Uuid;

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ManifestLease {
    pub owner: WorkerId,
    pub last_seen: jiff::Timestamp,
}

pub type ManifestName = String;

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(bound(serialize = "T: Serialize", deserialize = "T: DeserializeOwned"))]
pub struct Manifest<T>
where
    T: Specification,
{
    pub name: ManifestName,
    pub metadata: ManifestMetadata,
    pub spec: T,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ManifestStatus {
    pub status: ManifestStatusState,
    pub events: Vec<ManifestEvent>,

    pub changes: Vec<ManifestChangeEvent>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ManifestStatusState {
    Pending,
    Started,
    Errored,
    Running,
    Stopping,
    Deleting,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ManifestChangeEvent {
    pub created: jiff::Timestamp,
    pub handled: bool,
    pub event: ManifestChangeEventType,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum ManifestChangeEventType {
    Created,
    Changed,
    Deleted,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct ManifestEvent {
    pub owner: WorkerId,
    pub created: u64,
    pub message: String,
    pub state: Option<ManifestStatusState>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ManifestMetadata {}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Action {
    None,
    Requeue(std::time::Duration),
}
