package eu.osci.messagetypes;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.net.URL;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.junit.Test;
import org.w3._2000._09.xmldsig.ReferenceType;

import eu.osci._2008._05.common.urn.messagetypes.letterstyle.ObjectFactory;
import eu.osci.helper.CryptoTools;
import eu.osci.helper.EndpointLocator;
import eu.osci.messageparts.JaxbContextHolder;
import eu.osci.messageparts.OSCIFragmentCreator;


/**
 * Test class for the OSCIRequest.<br>
 * All relevant methods of the OSCIRequest class will be tested. To run this tests an implementation must be
 * installed under the URL declared in the static field MessageParent.URL_MSGBOX_HOST. The installation of the
 * msgBox must provide WSDLs under the URL MessageParent.URL_WSDL_USER, MessageParent.URL_WSDL_Synchron,
 * MessageParent.URL_WSDL_GERNERIC_USER, MessageParent.WSA_REPLY_TO. Some getter and setter Methods of the
 * OSCIRequest class will be tested in the TestOSCIMsgBoxFetchRequest class because it is easier to fetch the
 * message and check the set value in the fetched message.<br>
 * Copyright © 2010 Freie Hansestadt Bremen. <br>
 * Created by bremen online services Entwicklungs- und Betriebsgesellschaft mbH & Co. KG. <br>
 * This library (Starter Kit) can be used by anybody according to the Bremer Lizenz für freie
 * Softwarebibliotheken. The license terms can be viewed at www.osci.de or can be ordered at the
 * OSCI-Leitstelle (Die Senatorin für Finanzen, Freie Hansestadt Bremen, Postfach 10 15 40, 28015 Bremen).
 * 
 * @author R. Lindemann
 */
public class TestOSCIRequest extends MessageParent
{

  private static Logger LOG = Logger.getLogger(TestOSCIRequest.class.getName());

  // VM-Parameters for full logging
  // -Djava.util.logging.config.file=logging.properties
  // -Dcom.sun.xml.ws.transport.http.client.HttpTransportPipe.dump=true


  /**
   * Sends an OSCIRequest without any other configurations.<br>
   * Expected behaviour: Successful sending of the OSCI message without any exceptions and all optional header
   * fields must be null.
   */
  @Test
  public void testSimpleSend()
  {
    try
    {
      LOG.fine("Start testSimpleSend");
      EndpointLocator endpointLocator = new EndpointLocator(new URL(URL_WSDL_USER));
      endpointLocator.usePort("LetterStyle");
      OSCIRequest req = new OSCIRequest(endpointLocator);
      ByteArrayInputStream encData = new ByteArrayInputStream(XML_ENC_DATA.getBytes());
      OSCIResponse rsp = req.sendMessage(encData);
      assertEquals("AddionalHeader not 0", 0, req.getAdditionalHeaderList().size());
      assertEquals("AddionalHeader not 0", 0, rsp.getAdditionalHeaderList().size());
      assertEquals("AddionalHeader not 0", 0, req.getAdditionalHeaderList().size());
      assertNull("Delivery receipt is not null", rsp.getDeliveryReceipt());
      assertNotNull("RelatesTo is null", rsp.getRelatesTo());
      LOG.fine("MessageId: " + req.getMessageId());
      assertEquals("Not the correct RelatesTo in response", req.getMessageId().getValue(), rsp.getRelatesTo()
                                                                                              .getValue());
    }
    catch (Exception ex)
    {
      LOG.log(Level.SEVERE, "Error: ", ex);
      fail("Sending simple OSCI request failed. Error: " + ex.getMessage());
    }
  }

  /**
   * Sends a simple OSCI message to a generic endpoint. <br>
   * Expected behaviour: Successful sending of the OSCI message without any exceptions.
   */
  @Test
  public void testSimpleSendGeneric()
  {
    try
    {
      LOG.fine("Start testSimpleSendGeneric");
      EndpointLocator endpointLocator = new EndpointLocator(new URL(URL_WSDL_GERNERIC_USER));
      endpointLocator.usePort("LetterStyle");
      OSCIRequest req = new OSCIRequest(endpointLocator);
      ByteArrayInputStream encData = new ByteArrayInputStream(XML_ENC_DATA.getBytes());
      OSCIResponse rsp = req.sendMessage(encData);
      assertEquals("AddionalHeader not 0", 0, req.getAdditionalHeaderList().size());
      assertEquals("AddionalHeader not 0", 0, rsp.getAdditionalHeaderList().size());
      assertNull("Delivery receipt is not null", rsp.getDeliveryReceipt());
      assertNotNull("RelatesTo is null", rsp.getRelatesTo());
      assertEquals("Not the correct RelatesTo in response", req.getMessageId().getValue(), rsp.getRelatesTo()
                                                                                              .getValue());
    }
    catch (Exception ex)
    {
      LOG.log(Level.SEVERE, "Error: ", ex);
      fail("Sending simple OSCI request failed. Error: " + ex.getMessage());
    }
  }

  /**
   * Configures the addressing header fields and the OSCI header fields of an OSCI message. <br>
   * Expected behaviour: Successful sending of the OSCI message without any exceptions. The changed addressing
   * header should be the correct values.
   * 
   * @throws Exception Should be thrown in all error cases.
   */
  @Test
  public void testFields() throws Exception
  {
    LOG.fine("Start testAddressingFields");
    // String faultTo = "http://myFaultURI.com";
    String from = "http://myFromURI.com";
    String myMessageId = "MyMessageID123";
    String fetchURL = "http://fetchURL.de";
    String receURL = "http://receptionURL.de";
    String replyURL = "http://replyURL.de";
    GregorianCalendar expire = new GregorianCalendar();
    expire.setTime(new Date());
    // String typeOfBus = "myTpyOfBusCOn";
    X509Certificate cert = CryptoTools.loadCertificate(getClass().getResourceAsStream("/META-INF/test_osci-manager_cypher.cer"));

    ByteArrayInputStream encData = new ByteArrayInputStream(XML_ENC_DATA.getBytes());
    EndpointLocator endpointLocator = new EndpointLocator(new URL(URL_WSDL_USER));
    LOG.fine("Service: " + endpointLocator.getServiceList().length);
    endpointLocator.useTypeOfBusinessScenario(endpointLocator.getTypeOfBusinessScenarioList()[0]);
    OSCIRequest req = new OSCIRequest(endpointLocator);
    // Test all header elements
    // should better not set
    // req.setAction("")
    ArrayList<X509Certificate> certList = new ArrayList<X509Certificate>();
    certList.add(cert);
    req.addCompoundResult(createDummyCompoundResult(certList));
    req.setDeliveryReceiptDemand();
    // should not be used with http binding
    // req.setFaultTo(OSCIFragmentCreator.createEndpointRef(faultTo));
    req.setFetchedNotificationDemand(fetchURL);
    req.setFrom(OSCIFragmentCreator.createEndpointRef(from));

    // should better not set
    // req.setMessageId(_messageId);

    // will be created be set setMsgTimeStamps method
    // req.setMsgTimeStamps(_msgTime)
    req.setObsoleteAfter(expire.getTime());
    req.setReceptionReceiptDemand(false, false, receURL);

    // should better not set
    // req.setReplyTo(OSCIFragmentCreator.createEndpointRef(replyURL));

    // here a XKMS call must be done
    // req.setRequestSecurityTokenResponseCollectionType(new RequestSecurityTokenResponseCollectionType());

    // should better not set
    // req.setTo(_to);
    // should better not set
    // req.setTypeOfBusinessScenario(typeOfBus);
    ArrayList<X509Certificate> certs = new ArrayList<X509Certificate>();
    certs.add(cert);
    req.setX509TokenContainer(OSCIFragmentCreator.createOSCITokenContainer(certs));
    req.addRelatesTo(OSCIFragmentCreator.createRelatesTo(myMessageId));

    req.sendMessage(encData);
    assertEquals("AddionalHeader not 0", 0, req.getAdditionalHeaderList().size());

    req.getMessageId();
    // OSCIRequest reqSended = new OSCIRequest(req.sendSOAPMessage);
    assertNotNull("no compoundResult.", req.getCompoundResult());
    assertEquals("Wrong compoundResult count.", 1, req.getCompoundResult().size());

    assertNotNull("no dedlivery receipt header found.", req.getDeliveryReceiptDemand());

    assertNotNull("no Fetch Notifiaction header found.", req.getFetchedNotificationDemand());
    assertEquals("Wrong addressee for fetch notification.", fetchURL, req.getFetchedNotificationDemand()
                                                                         .getReplyTo()
                                                                         .getAddress()
                                                                         .getValue());

    assertNotNull("no from header found.", req.getFrom());
    assertEquals("Wrong url in from header.", from, req.getFrom().getAddress().getValue());

    assertNotNull("no msgTimeStamp header found.", req.getMsgTimeStamps());
    assertEquals("Wrong date for obsolate afer found.",
                 expire.get(Calendar.DAY_OF_YEAR),
                 req.getMsgTimeStamps().getObsoleteAfter().toGregorianCalendar().get(Calendar.DAY_OF_YEAR));

    assertNotNull("no reception receipt header found .", req.getReceptionReceiptDemand());
    assertEquals("Wrong echo request.", false, req.getReceptionReceiptDemand().isEchoRequest());
    assertEquals("Wrong crypto timestamp.", false, req.getReceptionReceiptDemand().isQualTSPForReceipt());
    assertEquals("Wrong reception receipt addressee.", receURL, req.getReceptionReceiptDemand()
                                                                   .getReplyTo()
                                                                   .getAddress()
                                                                   .getValue());

    assertNotNull("no type of bus con.", req.getTypeOfBusinessScenario());

    assertNotNull("no x509token container.", req.getX509TokenContainer());
    assertEquals("Wrong check certs.", 1, req.getX509TokenContainer().getX509TokenInfo().size());

    assertNotNull("no relatedTo.", req.getRelatesTo());
    assertEquals("Wrong relatesTo count.", 1, req.getRelatesTo().size());
    assertEquals("Wrong addressing entry for 'RelatesTo' element.", myMessageId, req.getRelatesTo()
                                                                                    .get(0)
                                                                                    .getValue());

    assertNotNull("Wrong addretype filter textssing entry for 'Action' element.", req.getAction().getValue());

    assertNotNull("Wrong addressing entry for 'MessageID' element.", req.getMessageId().getValue());
    assertNotSame("Wrong addressing entry for 'ReplyTo' element.", replyURL, req.getReplyTo()
                                                                                .getAddress()
                                                                                .getValue());
    assertNotNull("Wrong addressing entry for 'To' element.", req.getTo().getValue());
  }

  /**
   * Sends an OSCI message with a DeliveryReceiptDemand header element. <br>
   * Expected behaviour: Successful sending of the OSCI message without any exceptions. The DeliveryReceipt
   * must be included in the OSCIResponse.
   */
  @Test
  public void testSendWithDeliveryReceipt()
  {
    try
    {
      LOG.fine("Start testSendWithDeliveryReceipt");

      ByteArrayInputStream encData = new ByteArrayInputStream(XML_ENC_DATA.getBytes());
      EndpointLocator endpointLocator = new EndpointLocator(new URL(URL_WSDL_USER));
      endpointLocator.useTypeOfBusinessScenario("http://www.osci.eu/2008/05/common/urn/messageTypes/LetterStyle");
      OSCIRequest req = new OSCIRequest(endpointLocator);
      req.setDeliveryReceiptDemand();
      OSCIResponse rsp = req.sendMessage(encData);
      assertEquals("AddionalHeader not 0", 0, req.getAdditionalHeaderList().size());
      assertEquals("AddionalHeader not 0", 0, rsp.getAdditionalHeaderList().size());
      assertNotNull("Delivery receipt is null", rsp.getDeliveryReceipt());
      assertNotNull("Delivery ReceiptInfo is null", rsp.getDeliveryReceipt().getReceiptInfo());
      assertNotNull("Delivery Signature is null", rsp.getDeliveryReceipt().getSignature());
      assertNotNull("Delivery Reference List is null", rsp.getDeliveryReceipt()
                                                          .getSignature()
                                                          .getSignedInfo()
                                                          .getReference());
      List<ReferenceType> refs = rsp.getDeliveryReceipt().getSignature().getSignedInfo().getReference();
      assertNotNull("Delivery digest is null", refs.get(0).getDigestValue());
    }
    catch (Exception ex)
    {
      LOG.log(Level.SEVERE, "Error: ", ex);
      fail("Sending with delivery receipt request failed. Error: " + ex.getMessage());
    }
  }

  /**
   * Sends an OSCI message to a synchronous endpoint.<br>
   * Expected behaviour: Successful sending of the OSCI message without any exceptions. The OSCIResponse
   * message will include content in the body.
   */
  @Test
  public void testSynchronousSend()
  {
    LOG.fine("Start testSynchronousSend");
    try
    {
      EndpointLocator endpointLocator = new EndpointLocator(new URL(URL_WSDL_Synchron));
      endpointLocator.useTypeOfBusinessScenario("http://www.osci.eu/2008/05/common/urn/messageTypes/LetterStyle");
      OSCIRequest req = new OSCIRequest(endpointLocator);
      ByteArrayOutputStream out = new ByteArrayOutputStream();
      JaxbContextHolder.getLetterStyleJaxBContext()
                       .createMarshaller()
                       .marshal(new ObjectFactory().createLetterStyleMessage(createLetterStyleMsg()), out);
      OSCIResponse rsp = req.sendMessage(new ByteArrayInputStream(out.toByteArray()));
      assertEquals("AddionalHeader not 0", 0, req.getAdditionalHeaderList().size());
      assertEquals("AddionalHeader not 0", 0, rsp.getAdditionalHeaderList().size());
      ByteArrayOutputStream tmpOut = new ByteArrayOutputStream();
      rsp.getContent(tmpOut);
      assertNotNull("response body is null", tmpOut);
      assertTrue("Expected response text not found.", tmpOut.toString().indexOf("<LetterStyleMessage") > -1);
    }
    catch (Exception ex)
    {
      LOG.log(Level.SEVERE, "Error: ", ex);
      fail("Sending with delivery receipt request failed. Error: " + ex.getMessage());
    }
  }

  /**
   * Sends more messages with one SAML token and with derived keys. Use one instance of the OSCIRequest class
   * to send the OSCI messages.<br>
   * Expected behaviour: Successful sending of the OSCI message without any exceptions.
   * 
   * @throws Exception In all cases
   */
  @Test
  public void testSendMultiRequests() throws Exception
  {
    LOG.fine("Start testSendMultiRequests");
    String firstID;
    ByteArrayInputStream encData = new ByteArrayInputStream(XML_ENC_DATA.getBytes());
    EndpointLocator endpointLocator = new EndpointLocator(new URL(URL_WSDL_USER));
    endpointLocator.useTypeOfBusinessScenario("http://www.osci.eu/2008/05/common/urn/messageTypes/LetterStyle");
    OSCIRequest req = new OSCIRequest(endpointLocator);
    // req.setDeliveryReceiptDemand();
    OSCIResponse rsp = req.sendMessage(encData);
    assertEquals("AddionalHeader not 0", 0, req.getAdditionalHeaderList().size());
    assertEquals("AddionalHeader not 0", 0, rsp.getAdditionalHeaderList().size());
    assertNotNull("messageId is null", rsp.getMessageId());
    firstID = rsp.getMessageId().getValue();
    encData = new ByteArrayInputStream(XML_ENC_DATA.getBytes());
    rsp = req.sendMessage(encData);
    assertEquals("AddionalHeader not 0", 0, req.getAdditionalHeaderList().size());
    // req.setMessageId(_messageId)
    assertNotSame("MessageId is the same. ", firstID, rsp.getMessageId().getValue());
  }

}
