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

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import org.hibernate.annotations.UpdateTimestamp;

import java.time.OffsetDateTime;

/**
 * Persistent cache entry for JWT sub → display info resolve. See
 * migration 0023-user-display-cache.xml for the table contract and
 * the multi-tier resolve strategy.
 *
 * <p>Source semantics:
 * <ul>
 *   <li>{@link Source#JWT_CAPTURE} — written by JwtUserCaptureFilter
 *       on every authenticated request. Highest accuracy (claims as
 *       the user actually saw them).</li>
 *   <li>{@link Source#KEYCLOAK_SYNC} — written by the periodic
 *       Phase 3 scheduled bulk sync from Keycloak Admin API.</li>
 *   <li>{@link Source#KEYCLOAK_ON_DEMAND} — written when a cache miss
 *       triggers a one-shot Keycloak Admin lookup. Phase 2.</li>
 * </ul>
 */
@Entity
@Table(name = "user_display_cache")
public class UserDisplayCacheEntry {

  @Id
  @Column(name = "sub", nullable = false, length = 64)
  private String sub;

  @Column(name = "preferred_username", length = 255)
  private String preferredUsername;

  @Column(name = "name", length = 255)
  private String name;

  @Column(name = "email", length = 320)
  private String email;

  @Enumerated(EnumType.STRING)
  @Column(name = "source", nullable = false, length = 32)
  private Source source = Source.JWT_CAPTURE;

  @Column(name = "synced_at", nullable = false)
  private OffsetDateTime syncedAt;

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

  protected UserDisplayCacheEntry() {
    // JPA
  }

  public UserDisplayCacheEntry(String sub, String preferredUsername, String name,
                               String email, Source source, OffsetDateTime syncedAt) {
    this.sub = sub;
    this.preferredUsername = preferredUsername;
    this.name = name;
    this.email = email;
    this.source = source;
    this.syncedAt = syncedAt;
  }

  public String getSub() {
    return sub;
  }

  public String getPreferredUsername() {
    return preferredUsername;
  }

  public String getName() {
    return name;
  }

  public String getEmail() {
    return email;
  }

  public Source getSource() {
    return source;
  }

  public OffsetDateTime getSyncedAt() {
    return syncedAt;
  }

  public OffsetDateTime getUpdatedAt() {
    return updatedAt;
  }

  /**
   * Idempotent merge from a fresh capture / sync. Updates fields that
   * have a new non-null value; leaves existing data otherwise (e.g.
   * Keycloak sync may have richer email than JWT capture, don't blank).
   */
  public void mergeFrom(String preferredUsername, String name, String email,
                        Source source, OffsetDateTime syncedAt) {
    if (preferredUsername != null && !preferredUsername.isBlank()) {
      this.preferredUsername = preferredUsername;
    }
    if (name != null && !name.isBlank()) {
      this.name = name;
    }
    if (email != null && !email.isBlank()) {
      this.email = email;
    }
    this.source = source;
    this.syncedAt = syncedAt;
  }

  public enum Source {
    JWT_CAPTURE,
    KEYCLOAK_SYNC,
    KEYCLOAK_ON_DEMAND
  }
}
