//! Tenant registry types for IAM.
//!
//! Organizations and projects are the authoritative tenant entities for IAM.

use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// An organization tracked by IAM.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Organization {
    pub id: String,
    pub name: String,
    pub description: String,
    pub metadata: HashMap<String, String>,
    pub created_at: u64,
    pub updated_at: u64,
    pub enabled: bool,
}

impl Organization {
    pub fn new(id: impl Into<String>, name: impl Into<String>) -> Self {
        Self {
            id: id.into(),
            name: name.into(),
            description: String::new(),
            metadata: HashMap::new(),
            created_at: 0,
            updated_at: 0,
            enabled: true,
        }
    }

    pub fn with_description(mut self, description: impl Into<String>) -> Self {
        self.description = description.into();
        self
    }
}

/// A project belonging to an organization.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Project {
    pub id: String,
    pub org_id: String,
    pub name: String,
    pub description: String,
    pub metadata: HashMap<String, String>,
    pub created_at: u64,
    pub updated_at: u64,
    pub enabled: bool,
}

impl Project {
    pub fn new(
        id: impl Into<String>,
        org_id: impl Into<String>,
        name: impl Into<String>,
    ) -> Self {
        Self {
            id: id.into(),
            org_id: org_id.into(),
            name: name.into(),
            description: String::new(),
            metadata: HashMap::new(),
            created_at: 0,
            updated_at: 0,
            enabled: true,
        }
    }

    pub fn tenant_key(org_id: &str, project_id: &str) -> String {
        format!("{org_id}/{project_id}")
    }

    pub fn key(&self) -> String {
        Self::tenant_key(&self.org_id, &self.id)
    }

    pub fn with_description(mut self, description: impl Into<String>) -> Self {
        self.description = description.into();
        self
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn project_key_is_composite() {
        let project = Project::new("proj-1", "org-1", "Project 1");
        assert_eq!(project.key(), "org-1/proj-1");
    }
}
