#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AccessContext {
    pub tenant_id: Option<String>,
    pub workspace_id: String,
    pub actor_id: String,
    pub actor_type: String,
    pub source: String,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AccessDecision {
    Allow,
    Deny(AccessDenyReason),
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AccessDenyReason {
    MissingTenant,
    MissingWorkspace,
    PermissionDenied,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum JobStatus {
    Pending,
    Running,
    Succeeded,
    Failed,
    RetryScheduled,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct JobTicket {
    pub job_id: String,
    pub job_type: String,
    pub request_id: String,
    pub trace_id: String,
    pub workspace_id: String,
    pub status: JobStatus,
}

impl JobTicket {
    pub fn new(
        job_id: impl Into<String>,
        job_type: impl Into<String>,
        request_id: impl Into<String>,
        trace_id: impl Into<String>,
        workspace_id: impl Into<String>,
        status: JobStatus,
    ) -> Self {
        Self {
            job_id: job_id.into(),
            job_type: job_type.into(),
            request_id: request_id.into(),
            trace_id: trace_id.into(),
            workspace_id: workspace_id.into(),
            status,
        }
    }
}

pub fn decide_access(context: &AccessContext) -> AccessDecision {
    if context.tenant_id.is_none() {
        return AccessDecision::Deny(AccessDenyReason::MissingTenant);
    }
    if context.workspace_id.trim().is_empty() {
        return AccessDecision::Deny(AccessDenyReason::MissingWorkspace);
    }
    AccessDecision::Allow
}

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

    #[test]
    fn access_requires_tenant() {
        let context = AccessContext {
            tenant_id: None,
            workspace_id: "ws-1".to_string(),
            actor_id: "user-1".to_string(),
            actor_type: "human".to_string(),
            source: "react-next".to_string(),
        };
        assert_eq!(
            decide_access(&context),
            AccessDecision::Deny(AccessDenyReason::MissingTenant)
        );
    }

    #[test]
    fn access_allows_valid_context() {
        let context = AccessContext {
            tenant_id: Some("tenant-1".to_string()),
            workspace_id: "ws-1".to_string(),
            actor_id: "user-1".to_string(),
            actor_type: "human".to_string(),
            source: "react-next".to_string(),
        };
        assert_eq!(decide_access(&context), AccessDecision::Allow);
    }

    #[test]
    fn job_ticket_keeps_trace_fields() {
        let ticket = JobTicket::new(
            "job-1",
            "rebuild-index",
            "req-1",
            "trace-1",
            "ws-1",
            JobStatus::Pending,
        );
        assert_eq!(ticket.trace_id, "trace-1");
        assert_eq!(ticket.status, JobStatus::Pending);
    }
}
