use serde::{Deserialize, Serialize};
use time::OffsetDateTime;

use crate::ids::{AgentId, InvitationId, PlatformApiKeyId, UserId, WorkspaceId};

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum UserStatus {
    Active,
    Disabled,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum MembershipRole {
    Owner,
    Admin,
    Operator,
    Viewer,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum InvitationStatus {
    Pending,
    Accepted,
    Revoked,
    Expired,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PlatformApiKeyStatus {
    Active,
    Revoked,
}

#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum PlatformApiKeyScope {
    Read,
    Write,
    Deploy,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct User {
    pub id: UserId,
    pub email: String,
    pub display_name: String,
    pub status: UserStatus,
    #[serde(with = "time::serde::rfc3339")]
    pub created_at: OffsetDateTime,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Membership {
    pub workspace_id: WorkspaceId,
    pub user_id: UserId,
    pub role: MembershipRole,
    #[serde(with = "time::serde::rfc3339")]
    pub created_at: OffsetDateTime,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct InvitationToken {
    pub id: InvitationId,
    pub workspace_id: WorkspaceId,
    pub email: String,
    pub role: MembershipRole,
    pub status: InvitationStatus,
    pub token_hash: String,
    #[serde(with = "time::serde::rfc3339")]
    pub expires_at: OffsetDateTime,
    #[serde(with = "time::serde::rfc3339")]
    pub created_at: OffsetDateTime,
}

#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct PlatformApiKey {
    pub id: PlatformApiKeyId,
    pub workspace_id: WorkspaceId,
    #[serde(default, skip_serializing_if = "Option::is_none")]
    pub agent_id: Option<AgentId>,
    pub name: String,
    pub prefix: String,
    pub scopes: Vec<PlatformApiKeyScope>,
    pub status: PlatformApiKeyStatus,
    #[serde(with = "time::serde::rfc3339")]
    pub created_at: OffsetDateTime,
    #[serde(with = "time::serde::rfc3339::option")]
    pub last_used_at: Option<OffsetDateTime>,
}

#[cfg(test)]
mod tests {
    use serde_json::json;
    use time::{OffsetDateTime, format_description::well_known::Rfc3339};

    use super::{PlatformApiKey, PlatformApiKeyScope, PlatformApiKeyStatus, User, UserStatus};
    use crate::ids::{AgentId, PlatformApiKeyId, UserId, WorkspaceId};

    #[test]
    fn user_serializes_created_at_as_rfc3339() {
        let user = User {
            id: UserId::new("user_01"),
            email: "owner@example.com".to_owned(),
            display_name: "Owner".to_owned(),
            status: UserStatus::Active,
            created_at: OffsetDateTime::parse("2026-03-25T12:00:00Z", &Rfc3339).unwrap(),
        };

        let value = serde_json::to_value(&user).unwrap();

        assert_eq!(value["created_at"], json!("2026-03-25T12:00:00Z"));
    }

    #[test]
    fn user_deserializes_created_at_from_rfc3339() {
        let user: User = serde_json::from_value(json!({
            "id": "user_01",
            "email": "owner@example.com",
            "display_name": "Owner",
            "status": "active",
            "created_at": "2026-03-25T12:00:00Z"
        }))
        .unwrap();

        assert_eq!(
            user.created_at,
            OffsetDateTime::parse("2026-03-25T12:00:00Z", &Rfc3339).unwrap()
        );
    }

    #[test]
    fn platform_api_key_serializes_timestamps_as_rfc3339() {
        let api_key = PlatformApiKey {
            id: PlatformApiKeyId::new("pk_01"),
            workspace_id: WorkspaceId::new("ws_01"),
            agent_id: Some(AgentId::new("agent_01")),
            name: "Primary".to_owned(),
            prefix: "crk_live".to_owned(),
            scopes: vec![PlatformApiKeyScope::Read, PlatformApiKeyScope::Write],
            status: PlatformApiKeyStatus::Active,
            created_at: OffsetDateTime::parse("2026-03-25T12:01:00Z", &Rfc3339).unwrap(),
            last_used_at: Some(OffsetDateTime::parse("2026-03-25T12:05:00Z", &Rfc3339).unwrap()),
        };

        let value = serde_json::to_value(&api_key).unwrap();

        assert_eq!(value["created_at"], json!("2026-03-25T12:01:00Z"));
        assert_eq!(value["last_used_at"], json!("2026-03-25T12:05:00Z"));
    }
}
