package com.dalab.policyengine.model; import java.time.Instant; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.UUID; import org.hibernate.annotations.JdbcTypeCode; import org.hibernate.type.SqlTypes; import jakarta.persistence.CollectionTable; import jakarta.persistence.Column; import jakarta.persistence.ElementCollection; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.PrePersist; import jakarta.persistence.PreUpdate; import jakarta.persistence.Table; import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; /** * Entity representing a policy draft that goes through approval workflow. * Supports versioning, collaboration, and audit trails. */ @Entity @Table(name = "policy_drafts") public class PolicyDraft { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(columnDefinition = "UUID") private UUID id; /** * Name of the policy (must be unique when published). */ @NotBlank @Size(max = 255) @Column(nullable = false) private String name; /** * Detailed description of the policy's purpose and scope. */ @Size(max = 2000) @Column(columnDefinition = "TEXT") private String description; /** * Current status in the approval workflow. */ @Enumerated(EnumType.STRING) @Column(nullable = false) private PolicyDraftStatus status = PolicyDraftStatus.CREATED; /** * Version number for this draft (incremented on each revision). */ @Column(nullable = false) private Integer version = 1; /** * Reference to the published policy (if this is an update to existing policy). */ @Column(columnDefinition = "UUID") private UUID basePolicyId; /** * MVEL condition logic for the policy evaluation. */ @Column(columnDefinition = "TEXT") private String conditionLogic; /** * JSON representation of policy rules structure. * Stored as JSON to support complex rule configurations. */ @JdbcTypeCode(SqlTypes.JSON) @Column(columnDefinition = "jsonb") private List> rulesDefinition; /** * JSON representation of actions to be taken when policy is triggered. */ @JdbcTypeCode(SqlTypes.JSON) @Column(columnDefinition = "jsonb") private Map actions; /** * Change summary describing what was modified in this version. */ @Size(max = 1000) @Column(columnDefinition = "TEXT") private String changeSummary; /** * Justification for the policy changes or creation. */ @Size(max = 2000) @Column(columnDefinition = "TEXT") private String justification; /** * Expected impact of implementing this policy. */ @Size(max = 1000) @Column(columnDefinition = "TEXT") private String expectedImpact; /** * Target implementation date for the policy. */ private Instant targetImplementationDate; /** * Priority level for policy implementation (HIGH, MEDIUM, LOW). */ @Size(max = 50) private String priority = "MEDIUM"; /** * Business category or domain this policy applies to. */ @Size(max = 100) private String category; /** * Tags for categorization and searchability. */ @ElementCollection @CollectionTable(name = "policy_draft_tags", joinColumns = @JoinColumn(name = "draft_id")) @Column(name = "tag") private List tags = new ArrayList<>(); /** * Stakeholders who should be notified about this policy. */ @ElementCollection @CollectionTable(name = "policy_draft_stakeholders", joinColumns = @JoinColumn(name = "draft_id")) @Column(name = "stakeholder_id", columnDefinition = "UUID") private List stakeholders = new ArrayList<>(); /** * Approval workflow metadata. */ @JdbcTypeCode(SqlTypes.JSON) @Column(columnDefinition = "jsonb") private Map approvalMetadata; /** * Reviewer comments and feedback. */ @JdbcTypeCode(SqlTypes.JSON) @Column(columnDefinition = "jsonb") private List> reviewComments = new ArrayList<>(); // Audit fields @Column(nullable = false, updatable = false) private Instant createdAt; private Instant updatedAt; @Column(columnDefinition = "UUID", nullable = false) private UUID createdByUserId; @Column(columnDefinition = "UUID") private UUID updatedByUserId; private Instant submittedAt; @Column(columnDefinition = "UUID") private UUID submittedByUserId; private Instant approvedAt; @Column(columnDefinition = "UUID") private UUID approvedByUserId; private Instant rejectedAt; @Column(columnDefinition = "UUID") private UUID rejectedByUserId; private Instant publishedAt; @Column(columnDefinition = "UUID") private UUID publishedByUserId; // Constructors public PolicyDraft() {} public PolicyDraft(String name, String description, UUID createdByUserId) { this.name = name; this.description = description; this.createdByUserId = createdByUserId; } // Getters and Setters public UUID getId() { return id; } public void setId(UUID id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public PolicyDraftStatus getStatus() { return status; } public void setStatus(PolicyDraftStatus status) { this.status = status; } public Integer getVersion() { return version; } public void setVersion(Integer version) { this.version = version; } public UUID getBasePolicyId() { return basePolicyId; } public void setBasePolicyId(UUID basePolicyId) { this.basePolicyId = basePolicyId; } public String getConditionLogic() { return conditionLogic; } public void setConditionLogic(String conditionLogic) { this.conditionLogic = conditionLogic; } public List> getRulesDefinition() { return rulesDefinition; } public void setRulesDefinition(List> rulesDefinition) { this.rulesDefinition = rulesDefinition; } public Map getActions() { return actions; } public void setActions(Map actions) { this.actions = actions; } public String getChangeSummary() { return changeSummary; } public void setChangeSummary(String changeSummary) { this.changeSummary = changeSummary; } public String getJustification() { return justification; } public void setJustification(String justification) { this.justification = justification; } public String getExpectedImpact() { return expectedImpact; } public void setExpectedImpact(String expectedImpact) { this.expectedImpact = expectedImpact; } public Instant getTargetImplementationDate() { return targetImplementationDate; } public void setTargetImplementationDate(Instant targetImplementationDate) { this.targetImplementationDate = targetImplementationDate; } public String getPriority() { return priority; } public void setPriority(String priority) { this.priority = priority; } public String getCategory() { return category; } public void setCategory(String category) { this.category = category; } public List getTags() { return tags; } public void setTags(List tags) { this.tags = tags; } public List getStakeholders() { return stakeholders; } public void setStakeholders(List stakeholders) { this.stakeholders = stakeholders; } public Map getApprovalMetadata() { return approvalMetadata; } public void setApprovalMetadata(Map approvalMetadata) { this.approvalMetadata = approvalMetadata; } public List> getReviewComments() { return reviewComments; } public void setReviewComments(List> reviewComments) { this.reviewComments = reviewComments; } public Instant getCreatedAt() { return createdAt; } public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; } public Instant getUpdatedAt() { return updatedAt; } public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; } public UUID getCreatedByUserId() { return createdByUserId; } public void setCreatedByUserId(UUID createdByUserId) { this.createdByUserId = createdByUserId; } public UUID getUpdatedByUserId() { return updatedByUserId; } public void setUpdatedByUserId(UUID updatedByUserId) { this.updatedByUserId = updatedByUserId; } public Instant getSubmittedAt() { return submittedAt; } public void setSubmittedAt(Instant submittedAt) { this.submittedAt = submittedAt; } public UUID getSubmittedByUserId() { return submittedByUserId; } public void setSubmittedByUserId(UUID submittedByUserId) { this.submittedByUserId = submittedByUserId; } public Instant getApprovedAt() { return approvedAt; } public void setApprovedAt(Instant approvedAt) { this.approvedAt = approvedAt; } public UUID getApprovedByUserId() { return approvedByUserId; } public void setApprovedByUserId(UUID approvedByUserId) { this.approvedByUserId = approvedByUserId; } public Instant getRejectedAt() { return rejectedAt; } public void setRejectedAt(Instant rejectedAt) { this.rejectedAt = rejectedAt; } public UUID getRejectedByUserId() { return rejectedByUserId; } public void setRejectedByUserId(UUID rejectedByUserId) { this.rejectedByUserId = rejectedByUserId; } public Instant getPublishedAt() { return publishedAt; } public void setPublishedAt(Instant publishedAt) { this.publishedAt = publishedAt; } public UUID getPublishedByUserId() { return publishedByUserId; } public void setPublishedByUserId(UUID publishedByUserId) { this.publishedByUserId = publishedByUserId; } /** * Utility method to add a review comment. */ public void addReviewComment(String comment, UUID reviewerId, String reviewerRole) { Map commentData = Map.of( "comment", comment, "reviewerId", reviewerId.toString(), "reviewerRole", reviewerRole, "timestamp", Instant.now().toString() ); this.reviewComments.add(commentData); } /** * Utility method to add a tag if not already present. */ public void addTag(String tag) { if (!this.tags.contains(tag)) { this.tags.add(tag); } } /** * Utility method to add a stakeholder if not already present. */ public void addStakeholder(UUID stakeholderId) { if (!this.stakeholders.contains(stakeholderId)) { this.stakeholders.add(stakeholderId); } } @PrePersist protected void onCreate() { createdAt = Instant.now(); updatedAt = Instant.now(); } @PreUpdate protected void onUpdate() { updatedAt = Instant.now(); } }