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.OrganizationNature;
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.JoinColumn;
import jakarta.persistence.JoinTable;
import jakarta.persistence.Lob;
import jakarta.persistence.ManyToMany;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;

/**
 * Organization Type
 * The organization type is a setup that defines the usage of the Organization level and other configurations.
 */
@Entity
@Table(name = "organization_type")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@ResilientSecurityResourceConfig()
public class OrganizationType extends AbstractResilientLongIdEntity<OrganizationType> {

  private static final long serialVersionUID = 1L;

  /**
   * User defined code. Must be unique in the OrganizationType
   */
  @NotNull
  @Size(min = 1)
  @Column(name = "code", nullable = false, unique = true)
  private String code;

  /**
   * The name of the type. Must be unique in the system
   */
  @NotNull
  @Size(min = 3)
  @Column(name = "name", nullable = false, unique = true)
  private String name;

  /**
   * A more complete description of the type. Whats the meaning of this type?
   */
  @NotNull
  @Size(min = 3)
  @Column(name = "description", nullable = false)
  private String description;

  /**
   * The nature of the type
   */
  @NotNull
  @Enumerated(EnumType.STRING)
  @Column(name = "nature", nullable = false)
  private OrganizationNature nature;

  /**
   * Icon image associated with this organization type. Will be used in user interface.
   */
  @Lob
  @Column(name = "icon")
  private byte[] icon;

  @Column(name = "icon_content_type")
  private String iconContentType;

  @OneToMany(fetch = FetchType.LAZY, mappedBy = "organizationType")
  @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
  @JsonIgnoreProperties(value = { "user", "children", "inputData", "inputDataUploads", "parent",
      "organizationType" }, allowSetters = true)
  private Set<Organization> organizations = new HashSet<>();

  @ManyToMany(fetch = FetchType.LAZY)
  @JoinTable(name = "rel_organization_type__metadata_properties", joinColumns = @JoinColumn(name = "organization_type_id"), inverseJoinColumns = @JoinColumn(name = "metadata_properties_id"))
  @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
  @JsonIgnoreProperties(value = { "organizationTypes" }, allowSetters = true)
  private Set<MetadataProperty> metadataProperties = new HashSet<>();

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

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

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

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

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

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

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

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

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

  public OrganizationNature getNature() {
    return this.nature;
  }

  public OrganizationType nature(OrganizationNature nature) {
    this.setNature(nature);
    return this;
  }

  public void setNature(OrganizationNature nature) {
    this.nature = nature;
  }

  public byte[] getIcon() {
    return this.icon;
  }

  public OrganizationType icon(byte[] icon) {
    this.setIcon(icon);
    return this;
  }

  public void setIcon(byte[] icon) {
    this.icon = icon;
  }

  public String getIconContentType() {
    return this.iconContentType;
  }

  public OrganizationType iconContentType(String iconContentType) {
    this.iconContentType = iconContentType;
    return this;
  }

  public void setIconContentType(String iconContentType) {
    this.iconContentType = iconContentType;
  }

  public Set<Organization> getOrganizations() {
    return this.organizations;
  }

  public void setOrganizations(Set<Organization> organizations) {
    if (this.organizations != null) {
      this.organizations.forEach(i -> i.setOrganizationType(null));
    }
    if (organizations != null) {
      organizations.forEach(i -> i.setOrganizationType(this));
    }
    this.organizations = organizations;
  }

  public OrganizationType organizations(Set<Organization> organizations) {
    this.setOrganizations(organizations);
    return this;
  }

  public OrganizationType addOrganization(Organization organization) {
    this.organizations.add(organization);
    organization.setOrganizationType(this);
    return this;
  }

  public OrganizationType removeOrganization(Organization organization) {
    this.organizations.remove(organization);
    organization.setOrganizationType(null);
    return this;
  }

  public Set<MetadataProperty> getMetadataProperties() {
    return this.metadataProperties;
  }

  public void setMetadataProperties(Set<MetadataProperty> metadataProperties) {
    this.metadataProperties = metadataProperties;
  }

  public OrganizationType metadataProperties(Set<MetadataProperty> metadataProperties) {
    this.setMetadataProperties(metadataProperties);
    return this;
  }

  public OrganizationType addMetadataProperties(MetadataProperty metadataProperty) {
    this.metadataProperties.add(metadataProperty);
    return this;
  }

  public OrganizationType removeMetadataProperties(MetadataProperty metadataProperty) {
    this.metadataProperties.remove(metadataProperty);
    return this;
  }

//@formatter:off
  @Override
  public String toString() {
    return "OrganizationType{" +
              "id=" + getId() +
              ", code='" + getCode() + "'" +
              ", name='" + getName() + "'" +
              ", description='" + getDescription() + "'" +
              ", nature='" + getNature() + "'" +
              ", icon='" + getIcon() + "'" +
              ", iconContentType='" + getIconContentType() + "'" +
              ", version=" + getVersion() +
           "}";
  }
//@formatter:on
}
