//! Agent templates (global IDE store) and their monotonic versioning.

use serde::{Deserialize, Serialize};

use crate::error::DomainError;
use crate::ids::{ProfileId, TemplateId};
use crate::markdown::MarkdownDoc;

/// Monotonically increasing template version.
///
/// Invariant (enforced via [`TemplateVersion::next`]): a version only ever
/// increases by one when the template content changes (ARCHITECTURE §8.1).
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(transparent)]
pub struct TemplateVersion(pub u64);

impl TemplateVersion {
    /// The initial version assigned to a freshly created template.
    pub const INITIAL: Self = Self(1);

    /// Returns the next version (current + 1).
    #[must_use]
    pub const fn next(self) -> Self {
        Self(self.0 + 1)
    }

    /// Returns the raw version number.
    #[must_use]
    pub const fn get(self) -> u64 {
        self.0
    }
}

impl Default for TemplateVersion {
    fn default() -> Self {
        Self::INITIAL
    }
}

/// A reusable agent template stored in the global IDE store.
///
/// Invariants:
/// - `name` non-empty,
/// - `version` is monotonic; bumping is done via [`AgentTemplate::with_updated_content`].
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AgentTemplate {
    /// Stable identifier.
    pub id: TemplateId,
    /// Display name.
    pub name: String,
    /// Markdown content.
    pub content_md: MarkdownDoc,
    /// Current version.
    pub version: TemplateVersion,
    /// Default runtime profile for agents created from this template.
    pub default_profile_id: ProfileId,
}

impl AgentTemplate {
    /// Builds a validated template at [`TemplateVersion::INITIAL`].
    ///
    /// # Errors
    /// Returns [`DomainError::EmptyField`] if `name` is empty.
    pub fn new(
        id: TemplateId,
        name: impl Into<String>,
        content_md: MarkdownDoc,
        default_profile_id: ProfileId,
    ) -> Result<Self, DomainError> {
        let name = name.into();
        crate::validation::non_empty(&name, "template.name")?;
        Ok(Self {
            id,
            name,
            content_md,
            version: TemplateVersion::INITIAL,
            default_profile_id,
        })
    }

    /// Returns a copy of this template with new content and the version bumped
    /// by one, preserving the monotonic-version invariant.
    #[must_use]
    pub fn with_updated_content(&self, content_md: MarkdownDoc) -> Self {
        Self {
            content_md,
            version: self.version.next(),
            ..self.clone()
        }
    }
}
