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

import cloud.nstart.terravault.ordinis.domain.DataScope;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import org.hibernate.annotations.JdbcTypeCode;
import org.hibernate.type.SqlTypes;

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

/**
 * Webhook subscription — HTTP callback delivery параллельно с Kafka publishing.
 * Для consumer'ов которые не могут Kafka subscribe (геопортал PHP, legacy admin-ui'и
 * заказчиков, BI-системы).
 *
 * <p>Filter arrays: NULL/empty = match all events. Match logic в
 * {@code WebhookDispatcher} — event matches subscription iff
 * (scope_filter is null OR scope IN scope_filter) AND
 * (dictionary_filter is null OR dictionary_name IN dictionary_filter) AND
 * (event_type_filter is null OR event_type IN event_type_filter).
 *
 * <p>HMAC secret hex-encoded 64 chars (32 bytes random). Generated server-side
 * на create, returned ONCE в response, потом hidden — admin-ui показывает
 * только маскированный preview {@code sk_****abc123}.
 */
@Entity
@Table(name = "webhook_subscriptions")
public class WebhookSubscription {

  @Id
  @Column(name = "id")
  private UUID id;

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

  @Column(name = "url", nullable = false, length = 2048)
  private String url;

  /**
   * TEXT[] postgres хранит enum names (PUBLIC/INTERNAL/RESTRICTED).
   *
   * <p><b>Bug history:</b> раньше field был {@code List<DataScope>} +
   * {@code @JdbcTypeCode(SqlTypes.ARRAY)} — Hibernate сериализовал enum
   * как ordinal (e.g. {@code {0}} для PUBLIC), а DB CHECK constraint
   * ({@code chk_webhook_scope_filter}) требует string values из набора
   * {@code 'PUBLIC','INTERNAL','RESTRICTED'}. Insert падал с
   * {@code DataIntegrityViolationException} → 500 на POST с scopeFilter.
   *
   * <p><b>Fix:</b> field хранит {@code List<String>} (raw enum names).
   * Getter/setter конвертируют в/из {@link DataScope} — внешний API
   * (DTO + service signature + WebhookDispatcher) не меняется.
   */
  @JdbcTypeCode(SqlTypes.ARRAY)
  @Column(name = "scope_filter", columnDefinition = "TEXT[]")
  private List<String> scopeFilter;

  @JdbcTypeCode(SqlTypes.ARRAY)
  @Column(name = "dictionary_filter", columnDefinition = "TEXT[]")
  private List<String> dictionaryFilter;

  @JdbcTypeCode(SqlTypes.ARRAY)
  @Column(name = "event_type_filter", columnDefinition = "TEXT[]")
  private List<String> eventTypeFilter;

  @Column(name = "hmac_secret", nullable = false, length = 128)
  private String hmacSecret;

  @Column(name = "active", nullable = false)
  private boolean active = true;

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

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

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

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

  protected WebhookSubscription() {}

  public WebhookSubscription(
      String name,
      String url,
      String hmacSecret,
      String createdBy) {
    this.id = UUID.randomUUID();
    this.name = name;
    this.url = url;
    this.hmacSecret = hmacSecret;
    this.createdBy = createdBy;
    OffsetDateTime now = OffsetDateTime.now();
    this.createdAt = now;
    this.updatedAt = now;
  }

  public void touch() {
    this.updatedAt = OffsetDateTime.now();
  }

  public UUID getId() { return id; }
  public String getName() { return name; }
  public String getUrl() { return url; }
  public List<DataScope> getScopeFilter() {
    if (scopeFilter == null) return null;
    return scopeFilter.stream().map(DataScope::valueOf).toList();
  }
  public List<String> getDictionaryFilter() { return dictionaryFilter; }
  public List<String> getEventTypeFilter() { return eventTypeFilter; }
  public String getHmacSecret() { return hmacSecret; }
  public boolean isActive() { return active; }
  public OffsetDateTime getCreatedAt() { return createdAt; }
  public String getCreatedBy() { return createdBy; }
  public OffsetDateTime getUpdatedAt() { return updatedAt; }
  public String getDescription() { return description; }

  public void setName(String v) { this.name = v; touch(); }
  public void setUrl(String v) { this.url = v; touch(); }
  public void setScopeFilter(List<DataScope> v) {
    this.scopeFilter = (v == null) ? null : v.stream().map(Enum::name).toList();
    touch();
  }
  public void setDictionaryFilter(List<String> v) { this.dictionaryFilter = v; touch(); }
  public void setEventTypeFilter(List<String> v) { this.eventTypeFilter = v; touch(); }
  public void setActive(boolean v) { this.active = v; touch(); }
  public void setDescription(String v) { this.description = v; touch(); }
}
