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.OneToMany;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;

/**
 * Variable Scope
 * Example: GEE Scope 1/2/3 or General Info
 */
@Entity
@Table(name = "variable_scope")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@ResilientSecurityResourceConfig()
public class VariableScope extends AbstractResilientLongIdEntity<VariableScope> {

  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, unique = true)
  private String code;

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

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

  @OneToMany(fetch = FetchType.LAZY, mappedBy = "variableScope")
  @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
  @JsonIgnoreProperties(value = { "variables", "variableScope" }, allowSetters = true)
  private Set<VariableCategory> variableCategories = new HashSet<>();

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

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

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

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

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

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

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

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

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

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

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

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

  public Set<VariableCategory> getVariableCategories() {
    return this.variableCategories;
  }

  public void setVariableCategories(Set<VariableCategory> variableCategories) {
    if (this.variableCategories != null) {
      this.variableCategories.forEach(i -> i.setVariableScope(null));
    }
    if (variableCategories != null) {
      variableCategories.forEach(i -> i.setVariableScope(this));
    }
    this.variableCategories = variableCategories;
  }

  public VariableScope variableCategories(Set<VariableCategory> variableCategories) {
    this.setVariableCategories(variableCategories);
    return this;
  }

  public VariableScope addVariableCategory(VariableCategory variableCategory) {
    this.variableCategories.add(variableCategory);
    variableCategory.setVariableScope(this);
    return this;
  }

  public VariableScope removeVariableCategory(VariableCategory variableCategory) {
    this.variableCategories.remove(variableCategory);
    variableCategory.setVariableScope(null);
    return this;
  }

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

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

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

  public VariableScope addVariable(Variable variable) {
    this.variables.add(variable);
    variable.setVariableScope(this);
    return this;
  }

  public VariableScope removeVariable(Variable variable) {
    this.variables.remove(variable);
    variable.setVariableScope(null);
    return this;
  }

  public Boolean getHiddenForData() {
    return this.hiddenForData;
  }

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

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

  public Boolean getHiddenForFactor() {
    return this.hiddenForFactor;
  }

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

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

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