package com.oguerreiro.resilient.domain;

import java.util.HashSet;
import java.util.Set;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.oguerreiro.resilient.domain.enumeration.MetadataType;
import com.oguerreiro.resilient.security.custom.ResilientSecurityResourceConfig;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

/**
 * Metadata Property
 * Configuration of a metadata property. Defines its type and a description of its usage.
 */
@Entity
@Table(name = "metadata_property")
@ResilientSecurityResourceConfig()
public class MetadataProperty extends AbstractResilientLongIdEntity<MetadataProperty> {

  private static final long serialVersionUID = 1L;

  @NotNull
  @Size(min = 3)
  @Column(name = "code", nullable = false, unique = true)
  private String code;

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

  @Column(name = "description")
  private String description;

  @Column(name = "mandatory")
  private Boolean mandatory;

  @NotNull
  @Enumerated(EnumType.STRING)
  @Column(name = "metadata_type", nullable = false)
  private MetadataType metadataType;

  /**
   * Regex pattern for validation (optional). Only applies when the value is not empty.
   */
  @Column(name = "pattern")
  private String pattern;

  @ManyToMany(fetch = FetchType.LAZY, mappedBy = "metadataProperties")
  @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
  @JsonIgnoreProperties(value = { "organizations", "metadataProperties" }, allowSetters = true)
  private Set<OrganizationType> organizationTypes = new HashSet<>();

  public String getCode() {
    return this.code;
  }

  public MetadataProperty code(String code) {
    this.setCode(code);
    return this;
  }

  public void setCode(String code) {
    this.code = code;
  }

  public String getName() {
    return this.name;
  }

  public MetadataProperty name(String name) {
    this.setName(name);
    return this;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getDescription() {
    return this.description;
  }

  public MetadataProperty description(String description) {
    this.setDescription(description);
    return this;
  }

  public void setDescription(String description) {
    this.description = description;
  }

  public Boolean getMandatory() {
    return this.mandatory;
  }

  public MetadataProperty mandatory(Boolean mandatory) {
    this.setMandatory(mandatory);
    return this;
  }

  public void setMandatory(Boolean mandatory) {
    this.mandatory = mandatory;
  }

  public MetadataType getMetadataType() {
    return this.metadataType;
  }

  public MetadataProperty metadataType(MetadataType metadataType) {
    this.setMetadataType(metadataType);
    return this;
  }

  public void setMetadataType(MetadataType metadataType) {
    this.metadataType = metadataType;
  }

  public String getPattern() {
    return this.pattern;
  }

  public MetadataProperty pattern(String pattern) {
    this.setPattern(pattern);
    return this;
  }

  public void setPattern(String pattern) {
    this.pattern = pattern;
  }

  public Set<OrganizationType> getOrganizationTypes() {
    return this.organizationTypes;
  }

  public void setOrganizationTypes(Set<OrganizationType> organizationTypes) {
    if (this.organizationTypes != null) {
      this.organizationTypes.forEach(i -> i.removeMetadataProperties(this));
    }
    if (organizationTypes != null) {
      organizationTypes.forEach(i -> i.addMetadataProperties(this));
    }
    this.organizationTypes = organizationTypes;
  }

  public MetadataProperty organizationTypes(Set<OrganizationType> organizationTypes) {
    this.setOrganizationTypes(organizationTypes);
    return this;
  }

  public MetadataProperty addOrganizationType(OrganizationType organizationType) {
    this.organizationTypes.add(organizationType);
    organizationType.getMetadataProperties().add(this);
    return this;
  }

  public MetadataProperty removeOrganizationType(OrganizationType organizationType) {
    this.organizationTypes.remove(organizationType);
    organizationType.getMetadataProperties().remove(this);
    return this;
  }

//@formatter:off
  @Override
  public String toString() {
    return "MetadataProperty{" +
              "id=" + getId() +
              ", code='" + getCode() + "'" +
              ", name='" + getName() + "'" +
              ", description='" + getDescription() + "'" +
              ", mandatory='" + getMandatory() + "'" +
              ", metadataType='" + getMetadataType() + "'" +
              ", pattern='" + getPattern() + "'" +
              ", version=" + getVersion() +
           "}";
  }
//@formatter:on
}
