/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package de.christofreichardt.jaxws.securitypriceservice;

import de.christofreichardt.diagnosis.AbstractTracer;
import de.christofreichardt.diagnosis.LogLevel;
import de.christofreichardt.diagnosis.Traceable;
import de.christofreichardt.diagnosis.TracerFactory;
import de.christofreichardt.junit.MyTestComputer;
import de.christofreichardt.junit.MyTestSuite;
import de.christofreichardt.junit.TraceTestListener;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.xml.namespace.QName;
import javax.xml.ws.WebServiceException;
import org.codehaus.cargo.container.InstalledLocalContainer;
import org.codehaus.cargo.container.configuration.LocalConfiguration;
import org.codehaus.cargo.container.deployable.Deployable;
import org.codehaus.cargo.container.deployable.WAR;
import org.codehaus.cargo.container.wildfly.WildFly8xExistingLocalConfiguration;
import org.codehaus.cargo.container.wildfly.WildFly8xInstalledLocalContainer;
import org.junit.runner.JUnitCore;
import org.junit.runner.Result;
import org.junit.runner.RunWith;
import org.junit.runner.notification.Failure;
import org.junit.runners.Suite;

/**
 *
 * @author Christof Reichardt
 */
@RunWith(MyTestSuite.class)
@Suite.SuiteClasses({
  PriceRequestUnit.class
})
public class IntegrationTests implements Traceable {
  private InstalledLocalContainer installedLocalContainer;
//  final private String jdkHome;
  final private String wildFlyHome;
  final private Properties properties;
  final private String wsdlLocation;
  final private String proxyHost;
  final private String proxyPort;
  
  private IntegrationTests() {
    this.properties = readProperties();
//    this.jdkHome = this.properties.getProperty("de.christofreichardt.jaxws.securitypriceservice.jdk_home");
    this.wildFlyHome = this.properties.getProperty("de.christofreichardt.jaxws.securitypriceservice.wildfly_home");
    this.wsdlLocation = this.properties.getProperty("de.christofreichardt.jaxws.securitypriceservice.wsdl_location");
    this.proxyHost = this.properties.getProperty("de.christofreichardt.jaxws.securitypriceservice.http.proxyHost", null);
    this.proxyPort = this.properties.getProperty("de.christofreichardt.jaxws.securitypriceservice.http.proxyPort", "80");
  }
  
  private Properties readProperties() {
    AbstractTracer tracer = getCurrentTracer();
    tracer.entry("Properties", this, "readProperties()");
    
    try {
      Properties properties = new Properties();
      File propertyFile = new File("." + File.separator + "integrationtest.properties");
      try {
        try (FileInputStream inputStream = new FileInputStream(propertyFile)) {
          properties.load(inputStream);
        }
      }
      catch (IOException ex) {
        tracer.logException(LogLevel.ERROR, ex, getClass(), "readProperties()");
      }
      
      return properties;
    }
    finally {
      tracer.wayout();
    }
  }
  
  private void startWildFly() throws MalformedURLException, InterruptedException {
    AbstractTracer tracer = getCurrentTracer();
    tracer.entry("InstalledLocalContainer", this, "startWildFly()");
    
    try {
//      tracer.out().printfIndentln("IntegrationTests.getInstance().getJdkHome() = %s", this.jdkHome);
      tracer.out().printfIndentln("IntegrationTests.getInstance().getWildFlyHome() = %s", this.wildFlyHome);
      
      LocalConfiguration localConfiguration = new WildFly8xExistingLocalConfiguration(this.wildFlyHome + "/standalone");
      localConfiguration.setProperty("cargo.jboss.configuration", "standalone-full");
      Deployable deployable = new WAR("../SecurityPriceService-server/target/SecurityPriceService-server-0.0.1-SNAPSHOT.war");
      localConfiguration.addDeployable(deployable);
      this.installedLocalContainer = new WildFly8xInstalledLocalContainer(localConfiguration);
      this.installedLocalContainer.setHome(this.wildFlyHome);
      this.installedLocalContainer.setTimeout(30000);
      Map<String, String> systemProperties = new HashMap<>();
      systemProperties.put("java.net.preferIPv4Stack" , "true");
      if (this.proxyHost != null) {
        systemProperties.put("http.proxyHost", this.proxyHost);
        systemProperties.put("http.proxyPort", this.proxyPort);
      }
      this.installedLocalContainer.setSystemProperties(systemProperties);
      this.installedLocalContainer.start();
      
      URL url = new URL(this.wsdlLocation);
      QName qName = new QName("http://securitypriceservice.jaxws.christofreichardt.de/", "SecurityPriceService");
      int counter = 0;
      final int MAX_TRIALS = 5, DELAY = 2500;
      do {
        try {
          SecurityPriceService securityPriceService = new SecurityPriceService(url, qName);
          break;
        }
        catch (WebServiceException ex) {
          tracer.logMessage(LogLevel.INFO,"[" + counter + "] Pinging service ...", PriceRequestUnit.class, "void startWildfly()");
          if (counter == MAX_TRIALS) {
            tracer.logMessage(LogLevel.INFO,"Webservice isn't available after " + MAX_TRIALS + ". Giving up.", PriceRequestUnit.class, "void startWildfly()");
            throw ex;
          }
          Thread.sleep(DELAY);
          counter++;
        }
      } while (true);
    }
    finally {
      tracer.wayout();
    }
  }
  
  private void stopWildFly() {
    AbstractTracer tracer = getCurrentTracer();
    tracer.entry("void", this, "stopWildFly()");
    
    try {
      this.installedLocalContainer.stop();
    }
    finally {
      tracer.wayout();
    }
  }
  
  private void run() {
    AbstractTracer tracer = getCurrentTracer();
    tracer.entry("void", this, "run()");
    
    try {
      JUnitCore jUnitCore = new JUnitCore();
      jUnitCore.addListener(new TraceTestListener());
      Result result = jUnitCore.run(new MyTestComputer(this.properties), PriceRequestUnit.class);
      traceResults(result);
    }
    finally {
      tracer.wayout();
    }
  }
  
  private void traceResults(Result result) {
    AbstractTracer tracer = getCurrentTracer();
    tracer.entry("void", this, "traceResults(Result result)");

    try {
      tracer.out().printfIndentln("result.getRunCount() = %d", result.getRunCount());
      tracer.out().printfIndentln("result.getFailureCount() = %d", result.getFailureCount());
      tracer.out().printfIndentln("result.getRunTime() = %dms", result.getRunTime());
      
      System.out.printf("%nresult.getRunCount() = %d%n", result.getRunCount());
      System.out.printf("result.getFailureCount() = %d%n", result.getFailureCount());
      System.out.printf("result.getRunTime() = %dms%n%n", result.getRunTime());
      
      if (result.wasSuccessful()) {
        tracer.logMessage(LogLevel.INFO, "Test run has been successful.", getClass(), "traceResults(Result result)");
      }
      else {
        tracer.logMessage(LogLevel.WARNING, "Test run has failed.", getClass(), "traceResults(Result result)");
      }

      for (Failure failure : result.getFailures()) {
        tracer.out().printIndentln("+-----------------------------------------------------------------------------------------------------+");
        tracer.out().printfIndentln("%s[failure.getException() = %s]", failure.getDescription().getDisplayName(), failure.getException());
        
        Throwable throwable = failure.getException();
        do {
          StackTraceElement[] stackTraceElements = throwable.getStackTrace();
          for (StackTraceElement stackTraceElement : stackTraceElements) {
            tracer.out().printfIndentln("  at %s.%s(%s:%d)", stackTraceElement.getClassName(), stackTraceElement.getMethodName(), stackTraceElement.getFileName(), stackTraceElement.getLineNumber());
          }
          throwable = throwable.getCause();
          if (throwable != null)
            tracer.out().printfIndentln("caused by = %s: %s", throwable.getClass().getName(), throwable.getMessage());
        } while(throwable != null);
      }
    }
    finally {
      tracer.wayout();
    }
  }

  @Override
  public AbstractTracer getCurrentTracer() {
    return TracerFactory.getInstance().getCurrentPoolTracer();
  }
  
  /**
   * @param args the command line arguments
   * @throws de.christofreichardt.diagnosis.TracerFactory.Exception
   */
  public static void main(String[] args) throws TracerFactory.Exception {
    TracerFactory.getInstance().reset();
    InputStream resourceAsStream = IntegrationTests.class.getClassLoader().getResourceAsStream("de/christofreichardt/jaxws/securitypriceservice/trace-config.xml");
    if (resourceAsStream != null) {
      TracerFactory.getInstance().readConfiguration(resourceAsStream);
    }
    TracerFactory.getInstance().openPoolTracer();
    
    try {
      AbstractTracer tracer = TracerFactory.getInstance().getCurrentPoolTracer();
      tracer.initCurrentTracingContext();
      tracer.entry("void", IntegrationTests.class, "main(String[] args)");
      try {
        for (int i=0; i<args.length; i++) {
          tracer.out().printfIndentln("arg[%d] = %s", i, args[i]);
        }
        
        List<String> propertyNames = new ArrayList<>(System.getProperties().stringPropertyNames());
        Collections.sort(propertyNames);
        for (String propertyName : propertyNames) {
          tracer.out().printfIndentln("%s = %s", propertyName, System.getProperties().getProperty(propertyName));
        }
        
        IntegrationTests integrationTests = new IntegrationTests();
        try {
          integrationTests.startWildFly();
          integrationTests.run();
        }
        catch (InterruptedException | MalformedURLException ex) {
          tracer.logException(LogLevel.ERROR, ex, IntegrationTests.class, "void main(String[] args)");
        }
        finally {
          integrationTests.stopWildFly();
        }
      }
      finally {
        tracer.wayout();
      }
    }
    finally {
      TracerFactory.getInstance().closePoolTracer();
    }
  }
  
}
