package com.sun.identity.saml2.protocol.impl;

import com.sun.identity.saml2.assertion.Issuer;
import com.sun.identity.saml2.common.SAML2Exception;
import com.sun.identity.saml2.common.SAML2SDKUtils;
import com.sun.identity.saml2.protocol.Extensions;
import com.sun.identity.saml2.protocol.Status;
import com.sun.identity.saml2.protocol.StatusResponse;
import com.sun.identity.saml2.xmlsig.SigManager;
import com.sun.identity.shared.DateUtils;
import com.sun.identity.shared.xml.XMLUtils;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.text.ParseException;
import java.util.Date;
import java.util.List;
import org.w3c.dom.Element;

public abstract class StatusResponseImpl implements StatusResponse {
   protected String version;
   protected Date issueInstant;
   protected String destination;
   protected String signatureString;
   protected Extensions extensions;
   protected String consent;
   protected String inResponseTo;
   protected Status status;
   protected String responseId;
   protected Issuer issuer;
   protected boolean isSigned;
   protected Boolean isSignatureValid;
   protected boolean isMutable;
   protected PublicKey publicKey;
   protected String signedXMLString;

   @Override
   public String getVersion() {
      return this.version;
   }

   @Override
   public void setVersion(String value) throws SAML2Exception {
      if (this.isMutable) {
         this.version = value;
      } else {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("objectImmutable"));
      }
   }

   @Override
   public Date getIssueInstant() {
      return this.issueInstant;
   }

   @Override
   public void setIssueInstant(Date value) throws SAML2Exception {
      if (this.isMutable) {
         this.issueInstant = value;
      } else {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("objectImmutable"));
      }
   }

   @Override
   public String getDestination() {
      return this.destination;
   }

   @Override
   public void setDestination(String value) throws SAML2Exception {
      if (this.isMutable) {
         this.destination = value;
      } else {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("objectImmutable"));
      }
   }

   @Override
   public String getSignature() {
      return this.signatureString;
   }

   @Override
   public void sign(PrivateKey privateKey, X509Certificate cert) throws SAML2Exception {
      Element signatureEle = SigManager.getSigInstance().sign(this.toXMLString(true, true), this.getID(), privateKey, cert);
      this.signatureString = XMLUtils.print(signatureEle);
      this.signedXMLString = XMLUtils.print(signatureEle.getOwnerDocument().getDocumentElement(), "UTF-8");
      this.isSigned = true;
      this.makeImmutable();
   }

   @Override
   public Extensions getExtensions() {
      return this.extensions;
   }

   @Override
   public void setExtensions(Extensions value) throws SAML2Exception {
      if (this.isMutable) {
         this.extensions = value;
      } else {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("objectImmutable"));
      }
   }

   @Override
   public String getConsent() {
      return this.consent;
   }

   @Override
   public void setConsent(String value) throws SAML2Exception {
      if (this.isMutable) {
         this.consent = value;
      } else {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("objectImmutable"));
      }
   }

   @Override
   public String getInResponseTo() {
      return this.inResponseTo;
   }

   @Override
   public void setInResponseTo(String value) throws SAML2Exception {
      if (this.isMutable) {
         this.inResponseTo = value;
      } else {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("objectImmutable"));
      }
   }

   @Override
   public Status getStatus() {
      return this.status;
   }

   @Override
   public void setStatus(Status value) throws SAML2Exception {
      if (this.isMutable) {
         this.status = value;
      } else {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("objectImmutable"));
      }
   }

   @Override
   public String getID() {
      return this.responseId;
   }

   @Override
   public void setID(String value) throws SAML2Exception {
      if (this.isMutable) {
         this.responseId = value;
      } else {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("objectImmutable"));
      }
   }

   @Override
   public Issuer getIssuer() {
      return this.issuer;
   }

   @Override
   public void setIssuer(Issuer value) throws SAML2Exception {
      if (this.isMutable) {
         this.issuer = value;
      } else {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("objectImmutable"));
      }
   }

   @Override
   public boolean isSigned() {
      return this.isSigned;
   }

   @Override
   public boolean isSignatureValid(List<X509Certificate> senderCerts) throws SAML2Exception {
      if (this.isSignatureValid == null) {
         this.isSignatureValid = SigManager.getSigInstance().verify(this.signedXMLString, this.getID(), senderCerts);
      }

      return this.isSignatureValid;
   }

   @Override
   public String toXMLString() throws SAML2Exception {
      return this.toXMLString(true, false);
   }

   @Override
   public String toXMLString(boolean includeNSPrefix, boolean declareNS) throws SAML2Exception {
      StringBuilder xmlString = new StringBuilder(1000);
      if (declareNS) {
         xmlString.append(" xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"").append("\n");
      }

      xmlString.append("ID")
         .append("=")
         .append("\"")
         .append(this.responseId)
         .append("\"")
         .append(" ")
         .append("Version")
         .append("=")
         .append("\"")
         .append(this.version)
         .append("\"")
         .append(" ")
         .append("IssueInstant")
         .append("=")
         .append("\"")
         .append(DateUtils.toUTCDateFormat(this.issueInstant))
         .append("\"");
      if (this.destination != null && !this.destination.isEmpty()) {
         xmlString.append(" ").append("Destination").append("=").append("\"").append(this.destination).append("\"");
      }

      if (this.consent != null && !this.consent.isEmpty()) {
         xmlString.append(" ").append("Consent").append("=").append("\"").append(this.consent).append("\"");
      }

      if (this.inResponseTo != null && !this.inResponseTo.isEmpty()) {
         xmlString.append(" ").append("InResponseTo").append("=").append("\"").append(this.inResponseTo).append("\"");
      }

      xmlString.append(">");
      if (this.issuer != null) {
         String issuerString = this.issuer.toXMLString(includeNSPrefix, declareNS);
         xmlString.append("\n").append(issuerString);
      }

      if (this.signatureString != null && !this.signatureString.isEmpty()) {
         xmlString.append("\n").append(this.signatureString);
      }

      if (this.extensions != null) {
         xmlString.append("\n").append(this.extensions.toXMLString(includeNSPrefix, declareNS));
      }

      if (this.status != null) {
         xmlString.append("\n").append(this.status.toXMLString(includeNSPrefix, declareNS));
      }

      return xmlString.toString();
   }

   @Override
   public void makeImmutable() {
      if (this.isMutable) {
         if (this.issuer != null && this.issuer.isMutable()) {
            this.issuer.makeImmutable();
         }

         if (this.extensions != null && this.extensions.isMutable()) {
            this.extensions.makeImmutable();
         }

         if (this.status != null && this.status.isMutable()) {
            this.status.makeImmutable();
         }

         this.isMutable = false;
      }
   }

   @Override
   public boolean isMutable() {
      return this.isMutable;
   }

   protected void validateID(String responseId) throws SAML2Exception {
      if (responseId == null || responseId.isEmpty()) {
         SAML2SDKUtils.DEBUG.message("ID is missing in the SAMLResponse");
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("missingIDAttr"));
      }
   }

   protected void validateVersion(String version) throws SAML2Exception {
      if (version == null || version.isEmpty()) {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("missingVersion"));
      }

      if (!version.equals("2.0")) {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("incorrectVersion"));
      }
   }

   protected void validateIssueInstant(String issueInstantStr) throws SAML2Exception {
      if (issueInstantStr != null && !issueInstantStr.isEmpty()) {
         try {
            this.issueInstant = DateUtils.stringToDate(issueInstantStr);
         } catch (ParseException e) {
            SAML2SDKUtils.DEBUG.message("Error parsing IssueInstant", e);
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("incorrectIssueInstant"));
         }
      } else {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("missingIssueInstant"));
      }
   }

   protected void validateStatus() throws SAML2Exception {
      if (this.status == null) {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("missingStatus"));
      }
   }

   protected void validateData() throws SAML2Exception {
      this.validateID(this.responseId);
      this.validateVersion(this.version);
      if (this.issueInstant == null) {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("incorrectIssueInstant"));
      }

      this.validateIssueInstant(DateUtils.dateToString(this.issueInstant));
      this.validateStatus();
   }
}
