//! Strongly-typed identifiers.
//!
//! Each identifier is a `newtype` around [`uuid::Uuid`]. Using distinct types
//! per concept makes it impossible to pass, say, an [`AgentId`] where a
//! [`ProjectId`] is expected (compile-time safety, SOLID/typing discipline).

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

macro_rules! typed_id {
    ($(#[$meta:meta])* $name:ident) => {
        $(#[$meta])*
        #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
        #[serde(transparent)]
        pub struct $name(pub Uuid);

        impl $name {
            /// Wraps an existing [`Uuid`].
            #[must_use]
            pub const fn from_uuid(id: Uuid) -> Self {
                Self(id)
            }

            /// Generates a fresh random (v4) identifier.
            ///
            /// Prefer injecting an [`crate::ports::IdGenerator`] in application
            /// code for determinism; this convenience exists for tests and the
            /// composition root.
            #[must_use]
            pub fn new_random() -> Self {
                Self(Uuid::new_v4())
            }

            /// Returns the inner [`Uuid`].
            #[must_use]
            pub const fn as_uuid(&self) -> Uuid {
                self.0
            }
        }

        impl std::fmt::Display for $name {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                write!(f, "{}", self.0)
            }
        }

        impl From<Uuid> for $name {
            fn from(id: Uuid) -> Self {
                Self(id)
            }
        }
    };
}

typed_id!(
    /// Identifies a [`crate::project::Project`].
    ProjectId
);
typed_id!(
    /// Identifies an [`crate::agent::Agent`].
    AgentId
);
typed_id!(
    /// Identifies an [`crate::template::AgentTemplate`].
    TemplateId
);
typed_id!(
    /// Identifies an [`crate::profile::AgentProfile`].
    ProfileId
);
typed_id!(
    /// Identifies a local model server configuration.
    LocalModelServerId
);
typed_id!(
    /// Identifies a [`crate::skill::Skill`].
    SkillId
);
typed_id!(
    /// Identifies a [`crate::issue::Issue`].
    IssueId
);
typed_id!(
    /// Identifies a [`crate::sprint::Sprint`].
    SprintId
);
typed_id!(
    /// Identifies a [`crate::terminal::TerminalSession`].
    SessionId
);
typed_id!(
    /// Identifies a [`crate::layout::WindowId`]-bearing OS window.
    WindowId
);
typed_id!(
    /// Identifies a [`crate::layout::Tab`].
    TabId
);
typed_id!(
    /// Identifies one named terminal layout within a project (L10/#4).
    LayoutId
);
typed_id!(
    /// Identifies a node in a [`crate::layout::LayoutTree`].
    NodeId
);
typed_id!(
    /// Identifies one armed one-shot wake-up of a [`crate::ports::Scheduler`]
    /// (ARCHITECTURE §21.4). Opaque, cancellable handle returned by
    /// [`crate::ports::Scheduler::arm`] and consumed by
    /// [`crate::ports::Scheduler::cancel`].
    ScheduleId
);
typed_id!(
    /// Identifies a first-class background task.
    TaskId
);
typed_id!(
    /// Identifies one composite inter-agent business rendezvous.
    RendezvousId
);

/// Runtime-only key for an agent scoped by its project.
///
/// `AgentId` is persisted inside a project and is not globally unique across
/// simultaneously opened projects. Any app-wide in-memory registry that tracks
/// live runtime state for an agent must use this key instead of `AgentId` alone.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RuntimeAgentKey {
    /// Project that owns the runtime agent.
    pub project_id: ProjectId,
    /// Agent inside that project.
    pub agent_id: AgentId,
}

impl RuntimeAgentKey {
    /// Builds a scoped runtime key from its persisted identifiers.
    #[must_use]
    pub const fn new(project_id: ProjectId, agent_id: AgentId) -> Self {
        Self {
            project_id,
            agent_id,
        }
    }
}

impl std::fmt::Display for RuntimeAgentKey {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}:{}", self.project_id, self.agent_id)
    }
}
