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.security.custom.ResilientSecurityResourceConfig;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;

/**
 * Variable Category
 * Sub-division of VariableScope in Categories
 */
@Entity
@Table(name = "variable_category")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@ResilientSecurityResourceConfig()
public class VariableCategory extends AbstractResilientLongIdEntity<VariableCategory> {

  private static final long serialVersionUID = 1L;

  /**
   * Unique key, user defined.
   * Be aware, that this will be used to create the Variable.code
   * NOTE: The field type is string, but restricted to only numbers
   */
  @NotNull
  @Pattern(regexp = "^[0-9]*$")
  @Column(name = "code", nullable = false)
  private String code;

  /**
   * The name or title for this category
   */
  @NotNull
  @Column(name = "name", nullable = false)
  private String name;

  /**
   * A description or usage of this category
   */
  @NotNull
  @Column(name = "description", nullable = false)
  private String description;

  @OneToMany(fetch = FetchType.LAZY, mappedBy = "variableCategory")
  @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
  @JsonIgnoreProperties(value = { "variableClasses", "inputData", "outputData", "variableUnits", "baseUnit",
      "variableScope", "variableCategory" }, allowSetters = true)
  private Set<Variable> variables = new HashSet<>();

  @ManyToOne(fetch = FetchType.LAZY)
  @JsonIgnoreProperties(value = { "variableCategories", "variables" }, allowSetters = true)
  private VariableScope variableScope;

  @Column(name = "hidden_for_data")
  private Boolean hiddenForData;

  @Column(name = "hidden_for_factor")
  private Boolean hiddenForFactor;

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

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

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

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

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

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

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

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

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

  public Set<Variable> getVariables() {
    return this.variables;
  }

  public void setVariables(Set<Variable> variables) {
    if (this.variables != null) {
      this.variables.forEach(i -> i.setVariableCategory(null));
    }
    if (variables != null) {
      variables.forEach(i -> i.setVariableCategory(this));
    }
    this.variables = variables;
  }

  public VariableCategory variables(Set<Variable> variables) {
    this.setVariables(variables);
    return this;
  }

  public VariableCategory addVariable(Variable variable) {
    this.variables.add(variable);
    variable.setVariableCategory(this);
    return this;
  }

  public VariableCategory removeVariable(Variable variable) {
    this.variables.remove(variable);
    variable.setVariableCategory(null);
    return this;
  }

  public VariableScope getVariableScope() {
    return this.variableScope;
  }

  public void setVariableScope(VariableScope variableScope) {
    this.variableScope = variableScope;
  }

  public VariableCategory variableScope(VariableScope variableScope) {
    this.setVariableScope(variableScope);
    return this;
  }

  public Boolean getHiddenForData() {
    return hiddenForData;
  }

  public void setHiddenForData(Boolean hiddenForData) {
    this.hiddenForData = hiddenForData;
  }

  public VariableCategory hiddenForData(Boolean hiddenForData) {
    this.setHiddenForData(hiddenForData);
    return this;
  }

  public Boolean getHiddenForFactor() {
    return hiddenForFactor;
  }

  public void setHiddenForFactor(Boolean hiddenForFactor) {
    this.hiddenForFactor = hiddenForFactor;
  }

  public VariableCategory hiddenForFactor(Boolean hiddenForFactor) {
    this.setHiddenForFactor(hiddenForFactor);
    return this;
  }

  //@formatter:off
  @Override
  public String toString() {
    return "VariableCategory{" +
              "id=" + getId() +
              ", code='" + getCode() + "'" +
              ", name='" + getName() + "'" +
              ", description='" + getDescription() + "'" +
              ", version=" + getVersion() +
           "}";
  }
//@formatter:on
}
