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

import cloud.nstart.terravault.ordinis.domain.DataScope;
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.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.Version;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.annotations.Type;
import org.hibernate.envers.Audited;
import org.hibernate.envers.RelationTargetAuditMode;
import org.hibernate.type.SqlTypes;
import org.locationtech.jts.geom.Geometry;
import org.springframework.data.annotation.CreatedBy;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedBy;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

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

/**
 * Bitemporal entity справочной записи.
 *
 * <p>Две оси истории:
 * <ul>
 *   <li><b>Transaction time</b> — Hibernate Envers ({@code @Audited}). Когда мы
 *       <i>знали</i> о факте.</li>
 *   <li><b>Effective time</b> — {@code valid_from}/{@code valid_to}. Когда факт
 *       <i>действует</i> (например запись действует с 2027-01-01 но введена сегодня).</li>
 * </ul>
 *
 * EXCLUSION CONSTRAINT в БД (см. Liquibase 0003) гарантирует отсутствие overlap
 * по {@code (dictionary_id, business_key, valid_from..valid_to)}.
 */
@Entity
@Table(name = "dictionary_records")
@Audited(targetAuditMode = RelationTargetAuditMode.NOT_AUDITED)
@EntityListeners(AuditingEntityListener.class)
public class DictionaryRecord {

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

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

  @Column(name = "business_key", nullable = false, length = 256)
  private String businessKey;

  @Type(JsonType.class)
  @Column(name = "data", nullable = false, columnDefinition = "jsonb")
  private JsonNode data;

  @JdbcTypeCode(SqlTypes.GEOMETRY)
  @Column(name = "geometry", columnDefinition = "geometry(Geometry, 4326)")
  private Geometry geometry;

  @Enumerated(EnumType.STRING)
  @Column(name = "data_scope", nullable = false, length = 32)
  private DataScope dataScope;

  @Column(name = "valid_from", nullable = false)
  private OffsetDateTime validFrom;

  @Column(name = "valid_to", nullable = false)
  private OffsetDateTime validTo = OffsetDateTime.parse("9999-12-31T23:59:59Z");

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

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

  @CreatedBy
  @Column(name = "created_by", length = 128, updatable = false)
  private String createdBy;

  @LastModifiedBy
  @Column(name = "updated_by", length = 128)
  private String updatedBy;

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

  protected DictionaryRecord() {}

  public DictionaryRecord(
      UUID id,
      UUID dictionaryId,
      String businessKey,
      JsonNode data,
      DataScope dataScope,
      OffsetDateTime validFrom) {
    this.id = id;
    this.dictionaryId = dictionaryId;
    this.businessKey = businessKey;
    this.data = data;
    this.dataScope = dataScope;
    this.validFrom = validFrom;
  }

  public UUID getId() { return id; }
  public UUID getDictionaryId() { return dictionaryId; }
  public String getBusinessKey() { return businessKey; }
  public JsonNode getData() { return data; }
  public Geometry getGeometry() { return geometry; }
  public DataScope getDataScope() { return dataScope; }
  public OffsetDateTime getValidFrom() { return validFrom; }
  public OffsetDateTime getValidTo() { return validTo; }
  public OffsetDateTime getCreatedAt() { return createdAt; }
  public OffsetDateTime getUpdatedAt() { return updatedAt; }
  public String getCreatedBy() { return createdBy; }
  public String getUpdatedBy() { return updatedBy; }
  public Long getVersion() { return version; }

  public void setData(JsonNode v) { this.data = v; }
  public void setGeometry(Geometry v) { this.geometry = v; }
  public void setValidFrom(OffsetDateTime v) { this.validFrom = v; }
  public void setValidTo(OffsetDateTime v) { this.validTo = v; }
  public void setDataScope(DataScope v) { this.dataScope = v; }
}
