package com.oguerreiro.resilient.domain;

import java.io.Serializable;
import java.time.Instant;
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.activity.AbstractActivityDomain;
import com.oguerreiro.resilient.domain.enumeration.UploadStatus;
import com.oguerreiro.resilient.domain.enumeration.UploadType;
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.Lob;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.OneToMany;
import jakarta.persistence.Table;
import jakarta.validation.constraints.NotNull;

/**
 * Input data upload file Users can upload an Excel file with InputData that
 * will automatically insert values in the table InputData
 */
@Entity
@Table(name = "input_data_upload")
@ResilientSecurityResourceConfig()
public class InputDataUpload extends AbstractActivityDomain<InputDataUpload> implements Serializable {

  private static final long serialVersionUID = 1L;

  /**
   * A simple text with a title, for human reference and better relation
   */
  @NotNull
  @Column(name = "title", nullable = false)
  private String title;

  /**
   * An Excel file, that must match a template for input
   */
  @Lob
  @Column(name = "data_file", nullable = false)
  private byte[] dataFile;

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

  /**
   * The name of the uploaded Excel file
   */
  @Column(name = "upload_file_name")
  private String uploadFileName;

  /**
   * The generated name of the Excel file, used to save to disk
   */
  @Column(name = "disk_file_name")
  private String diskFileName;

  /**
   * Type of the Upload
   */
  @NotNull
  @Enumerated(EnumType.STRING)
  @Column(name = "type", nullable = false)
  private UploadType type;

  /**
   * State of the Upload
   */
  @NotNull
  @Enumerated(EnumType.STRING)
  @Column(name = "state", nullable = false)
  private UploadStatus state;

  /**
   * Optional comments
   */
  @Column(name = "comments")
  private String comments;

  /**
   * The creation date (insert). When the data was inserted into the system.
   */
  @NotNull
  @Column(name = "creation_date", nullable = false)
  private Instant creationDate;

  /**
   * The actual username logged into the system, that inserted the data. NOT a
   * reference to the user, but the login username.
   */
  @NotNull
  @Column(name = "creation_username", nullable = false)
  private String creationUsername;

  @OneToMany(fetch = FetchType.LAZY, mappedBy = "sourceInputDataUpload")
  @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
  @JsonIgnoreProperties(value = { "inputData", "variable", "period", "sourceUnit", "unit", "owner", "sourceInputData",
      "sourceInputDataUpload", }, allowSetters = true)
  private Set<InputData> inputData = new HashSet<>();

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

  @ManyToOne(fetch = FetchType.LAZY)
  @JsonIgnoreProperties(value = { "periodVersions" }, allowSetters = true)
  private Period period;

  @ManyToOne(fetch = FetchType.LAZY)
  @JsonIgnoreProperties(value = { "user", "children", "parent", "organizationType" }, allowSetters = true)
  private Organization owner;

  public String getTitle() {
    return this.title;
  }

  public InputDataUpload title(String title) {
    this.setTitle(title);
    return this;
  }

  public void setTitle(String title) {
    this.title = title;
  }

  public byte[] getDataFile() {
    return this.dataFile;
  }

  public InputDataUpload dataFile(byte[] dataFile) {
    this.setDataFile(dataFile);
    return this;
  }

  public void setDataFile(byte[] dataFile) {
    this.dataFile = dataFile;
  }

  public String getDataFileContentType() {
    return this.dataFileContentType;
  }

  public InputDataUpload dataFileContentType(String dataFileContentType) {
    this.dataFileContentType = dataFileContentType;
    return this;
  }

  public void setDataFileContentType(String dataFileContentType) {
    this.dataFileContentType = dataFileContentType;
  }

  public String getUploadFileName() {
    return this.uploadFileName;
  }

  public InputDataUpload uploadFileName(String uploadFileName) {
    this.setUploadFileName(uploadFileName);
    return this;
  }

  public void setUploadFileName(String uploadFileName) {
    this.uploadFileName = uploadFileName;
  }

  public String getDiskFileName() {
    return this.diskFileName;
  }

  public InputDataUpload diskFileName(String diskFileName) {
    this.setDiskFileName(diskFileName);
    return this;
  }

  public void setDiskFileName(String diskFileName) {
    this.diskFileName = diskFileName;
  }

  public UploadType getType() {
    return this.type;
  }

  public InputDataUpload type(UploadType type) {
    this.setType(type);
    return this;
  }

  public void setType(UploadType type) {
    this.type = type;
  }

  public UploadStatus getState() {
    return this.state;
  }

  public InputDataUpload state(UploadStatus state) {
    this.setState(state);
    return this;
  }

  public void setState(UploadStatus state) {
    this.state = state;
  }

  public String getComments() {
    return this.comments;
  }

  public InputDataUpload comments(String comments) {
    this.setComments(comments);
    return this;
  }

  public void setComments(String comments) {
    this.comments = comments;
  }

  public Instant getCreationDate() {
    return this.creationDate;
  }

  public InputDataUpload creationDate(Instant creationDate) {
    this.setCreationDate(creationDate);
    return this;
  }

  public void setCreationDate(Instant creationDate) {
    this.creationDate = creationDate;
  }

  public String getCreationUsername() {
    return this.creationUsername;
  }

  public InputDataUpload creationUsername(String creationUsername) {
    this.setCreationUsername(creationUsername);
    return this;
  }

  public void setCreationUsername(String creationUsername) {
    this.creationUsername = creationUsername;
  }

  public Set<InputData> getInputData() {
    return this.inputData;
  }

  public void setInputData(Set<InputData> inputData) {
    if (this.inputData != null) {
      this.inputData.forEach(i -> i.setSourceInputDataUpload(null));
    }
    if (inputData != null) {
      inputData.forEach(i -> i.setSourceInputDataUpload(this));
    }
    this.inputData = inputData;
  }

  public InputDataUpload inputData(Set<InputData> inputData) {
    this.setInputData(inputData);
    return this;
  }

  public InputDataUpload addInputData(InputData inputData) {
    this.inputData.add(inputData);
    inputData.setSourceInputDataUpload(this);
    return this;
  }

  public InputDataUpload removeInputData(InputData inputData) {
    this.inputData.remove(inputData);
    inputData.setSourceInputDataUpload(null);
    return this;
  }

  public Set<InputDataUploadLog> getInputDataUploadLogs() {
    return this.inputDataUploadLogs;
  }

  public void setInputDataUploadLogs(Set<InputDataUploadLog> inputDataUploadLogs) {
    if (this.inputDataUploadLogs != null) {
      this.inputDataUploadLogs.forEach(i -> i.setInputDataUpload(null));
    }
    if (inputDataUploadLogs != null) {
      inputDataUploadLogs.forEach(i -> i.setInputDataUpload(this));
    }
    this.inputDataUploadLogs = inputDataUploadLogs;
  }

  public InputDataUpload inputDataUploadLogs(Set<InputDataUploadLog> inputDataUploadLogs) {
    this.setInputDataUploadLogs(inputDataUploadLogs);
    return this;
  }

  public InputDataUpload addInputDataUploadLog(InputDataUploadLog inputDataUploadLog) {
    this.inputDataUploadLogs.add(inputDataUploadLog);
    inputDataUploadLog.setInputDataUpload(this);
    return this;
  }

  public InputDataUpload removeInputDataUploadLog(InputDataUploadLog inputDataUploadLog) {
    this.inputDataUploadLogs.remove(inputDataUploadLog);
    inputDataUploadLog.setInputDataUpload(null);
    return this;
  }

  public Period getPeriod() {
    return this.period;
  }

  public void setPeriod(Period period) {
    this.period = period;
  }

  public InputDataUpload period(Period period) {
    this.setPeriod(period);
    return this;
  }

  public Organization getOwner() {
    return this.owner;
  }

  public void setOwner(Organization organization) {
    this.owner = organization;
  }

  public InputDataUpload owner(Organization organization) {
    this.setOwner(organization);
    return this;
  }

//@formatter:off
  @Override
  public String toString() {
    return "InputDataUpload{" + 
              "id=" + getId() + 
              ", title='" + getTitle() + "'" + 
              ", dataFile='" + getDataFile() + "'" + 
              ", dataFileContentType='" + getDataFileContentType() + "'" + 
              ", uploadFileName='" + getUploadFileName() + "'" + 
              ", diskFileName='" + getDiskFileName() + "'" + 
              ", type='" + getType() + "'" + 
              ", state='" + getState() + "'" + 
              ", comments='" + getComments() + "'" + 
              ", creationDate='" + getCreationDate() + "'" +
              ", creationUsername='" + getCreationUsername() + "'" + 
              ", version=" + getVersion() + 
           "}";
  }
//@formatter:on
}
