package cloud.nstart.terravault.ordinis.domain.schemaworkflow;

import com.fasterxml.jackson.databind.JsonNode;
import io.hypersistence.utils.hibernate.type.json.JsonType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.Id;
import jakarta.persistence.PrePersist;
import jakarta.persistence.PreUpdate;
import jakarta.persistence.Table;
import jakarta.persistence.Version;
import org.hibernate.annotations.Type;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.OffsetDateTime;
import java.util.UUID;

/**
 * Dictionary schema-level approval workflow draft. Параллель к
 * {@link cloud.nstart.terravault.ordinis.domain.draft.RecordDraft}, но
 * proposes изменение SCHEMA (а не отдельной записи).
 *
 * <p>Lifecycle (см. {@link SchemaDraftStatus}):
 * draft → review_pending → {approved, changes_requested, rejected, withdrawn}
 * → published / withdrawn.
 *
 * <p>Один active draft per dict (Liquibase 0022 EXCLUDE constraint
 * {@code schema_drafts_one_active_per_dict}) — schema changes coarse-grained,
 * параллельное редактирование не имеет смысла.
 *
 * <p>{@code status} хранится lowercase в БД (см. migration check constraint
 * — 'draft', 'review_pending', etc), поэтому используем String column +
 * runtime convert через {@link SchemaDraftStatus#dbValue()} /
 * {@link SchemaDraftStatus#fromDbValue(String)} вместо EnumType.STRING.
 * Это match'ит naming convention из 0019/0020 record_drafts.
 */
@Entity
@Table(name = "dictionary_schema_drafts")
@EntityListeners(AuditingEntityListener.class)
public class DictionarySchemaDraft {

  @Id
  @Column(name = "id", nullable = false, updatable = false)
  private UUID id;

  @Column(name = "dictionary_id", nullable = false)
  private UUID dictionaryId;

  /** Полное proposed schema content (JSON Schema document). */
  @Type(JsonType.class)
  @Column(name = "proposed_schema", columnDefinition = "jsonb", nullable = false)
  private JsonNode proposedSchema;

  /** Live HEAD schema version, от которого ответвлён этот draft. */
  @Column(name = "branched_from_version", nullable = false, length = 32)
  private String branchedFromVersion;

  @Column(name = "reason", columnDefinition = "TEXT")
  private String reason;

  /**
   * Назначенные reviewer'ы (JSONB array of user IDs). NULL = open pool —
   * любой user с approve role может decide. Set только когда maker явно
   * передаёт {@code reviewers} в submit-for-review request.
   */
  @Type(JsonType.class)
  @Column(name = "reviewer_assignments", columnDefinition = "jsonb")
  private JsonNode reviewerAssignments;

  @Column(name = "review_note", columnDefinition = "TEXT")
  private String reviewNote;

  /** Status хранится lowercase. Get/set через {@link #getStatus()} / {@link #setStatus(SchemaDraftStatus)}. */
  @Column(name = "status", nullable = false, length = 24)
  private String statusDb;

  @Column(name = "maker_id", nullable = false, length = 128, updatable = false)
  private String makerId;

  @Column(name = "created_at", nullable = false, updatable = false)
  private OffsetDateTime createdAt;

  @Column(name = "submitted_at")
  private OffsetDateTime submittedAt;

  @Column(name = "decision_user_id", length = 128)
  private String decisionUserId;

  @Column(name = "decision_at")
  private OffsetDateTime decisionAt;

  @Column(name = "decision_comment", columnDefinition = "TEXT")
  private String decisionComment;

  @Column(name = "published_at")
  private OffsetDateTime publishedAt;

  @Column(name = "published_version", length = 32)
  private String publishedVersion;

  @Column(name = "publish_note", columnDefinition = "TEXT")
  private String publishNote;

  @Column(name = "updated_at", nullable = false)
  private OffsetDateTime updatedAt;

  @Version
  @Column(name = "version")
  private Long version;

  protected DictionarySchemaDraft() {}

  public DictionarySchemaDraft(
      UUID id,
      UUID dictionaryId,
      JsonNode proposedSchema,
      String branchedFromVersion,
      String makerId,
      String reason) {
    this.id = id;
    this.dictionaryId = dictionaryId;
    this.proposedSchema = proposedSchema;
    this.branchedFromVersion = branchedFromVersion;
    this.makerId = makerId;
    this.reason = reason;
    this.statusDb = SchemaDraftStatus.DRAFT.dbValue();
    var now = OffsetDateTime.now();
    this.createdAt = now;
    this.updatedAt = now;
  }

  @PrePersist
  void prePersist() {
    if (createdAt == null) createdAt = OffsetDateTime.now();
    if (updatedAt == null) updatedAt = createdAt;
    if (statusDb == null) statusDb = SchemaDraftStatus.DRAFT.dbValue();
  }

  @PreUpdate
  void preUpdate() {
    updatedAt = OffsetDateTime.now();
  }

  public UUID getId() { return id; }
  public UUID getDictionaryId() { return dictionaryId; }
  public JsonNode getProposedSchema() { return proposedSchema; }
  public String getBranchedFromVersion() { return branchedFromVersion; }
  public String getReason() { return reason; }
  public JsonNode getReviewerAssignments() { return reviewerAssignments; }
  public String getReviewNote() { return reviewNote; }

  public SchemaDraftStatus getStatus() { return SchemaDraftStatus.fromDbValue(statusDb); }
  public void setStatus(SchemaDraftStatus s) { this.statusDb = s.dbValue(); }

  public String getMakerId() { return makerId; }
  public OffsetDateTime getCreatedAt() { return createdAt; }
  public OffsetDateTime getSubmittedAt() { return submittedAt; }
  public String getDecisionUserId() { return decisionUserId; }
  public OffsetDateTime getDecisionAt() { return decisionAt; }
  public String getDecisionComment() { return decisionComment; }
  public OffsetDateTime getPublishedAt() { return publishedAt; }
  public String getPublishedVersion() { return publishedVersion; }
  public String getPublishNote() { return publishNote; }
  public OffsetDateTime getUpdatedAt() { return updatedAt; }
  public Long getVersion() { return version; }

  public void setProposedSchema(JsonNode v) { this.proposedSchema = v; }
  public void setReason(String v) { this.reason = v; }
  public void setReviewerAssignments(JsonNode v) { this.reviewerAssignments = v; }
  public void setReviewNote(String v) { this.reviewNote = v; }
  public void setSubmittedAt(OffsetDateTime v) { this.submittedAt = v; }
  public void setDecisionUserId(String v) { this.decisionUserId = v; }
  public void setDecisionAt(OffsetDateTime v) { this.decisionAt = v; }
  public void setDecisionComment(String v) { this.decisionComment = v; }
  public void setPublishedAt(OffsetDateTime v) { this.publishedAt = v; }
  public void setPublishedVersion(String v) { this.publishedVersion = v; }
  public void setPublishNote(String v) { this.publishNote = v; }
}
