package ca.uhn.fhir.jpa.starter;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.jpa.model.util.ProviderConstants;
import ca.uhn.fhir.rest.api.CacheControlDirective;
import ca.uhn.fhir.rest.client.api.IGenericClient;
import ca.uhn.fhir.rest.client.api.ServerValidationModeEnum;
import ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor;
import ca.uhn.fhir.rest.client.interceptor.UrlTenantSelectionInterceptor;
import ca.uhn.fhir.test.utilities.JettyUtil;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.webapp.WebAppContext;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.IntegerType;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Patient;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;

import java.nio.file.Paths;

import static org.junit.Assert.assertEquals;

public class MultitenantServerR4IT {

  private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(MultitenantServerR4IT.class);
  private static IGenericClient ourClient;
  private static FhirContext ourCtx;
  private static int ourPort;
  private static Server ourServer;
  private static UrlTenantSelectionInterceptor ourClientTenantInterceptor;

  static {
    HapiProperties.forceReload();
    HapiProperties.setProperty(HapiProperties.DATASOURCE_URL, "jdbc:h2:mem:dbr4-mt");
    HapiProperties.setProperty(HapiProperties.FHIR_VERSION, "R4");
    HapiProperties.setProperty(HapiProperties.SUBSCRIPTION_WEBSOCKET_ENABLED, "true");
    HapiProperties.setProperty(HapiProperties.PARTITIONING_MULTITENANCY_ENABLED, "true");
    ourCtx = FhirContext.forR4();
  }

  @Test
  public void testCreateAndReadInTenantA() {
    ourLog.info("Base URL is: " + HapiProperties.getServerAddress());

    // Create tenant A
    ourClientTenantInterceptor.setTenantId("DEFAULT");
    ourClient
      .operation()
      .onServer()
      .named(ProviderConstants.PARTITION_MANAGEMENT_CREATE_PARTITION)
      .withParameter(Parameters.class, ProviderConstants.PARTITION_MANAGEMENT_PARTITION_ID, new IntegerType(1))
      .andParameter(ProviderConstants.PARTITION_MANAGEMENT_PARTITION_NAME, new CodeType("TENANT-A"))
      .execute();


    ourClientTenantInterceptor.setTenantId("TENANT-A");
    Patient pt = new Patient();
    pt.addName().setFamily("Family A");
    ourClient.create().resource(pt).execute().getId();

    Bundle searchResult = ourClient.search().forResource(Patient.class).returnBundle(Bundle.class).cacheControl(new CacheControlDirective().setNoCache(true)).execute();
    assertEquals(1, searchResult.getEntry().size());
    Patient pt2 = (Patient) searchResult.getEntry().get(0).getResource();
    assertEquals("Family A", pt2.getName().get(0).getFamily());
  }

  @Test
  public void testCreateAndReadInTenantB() {
    ourLog.info("Base URL is: " + HapiProperties.getServerAddress());

    // Create tenant A
    ourClientTenantInterceptor.setTenantId("DEFAULT");
    ourClient
      .operation()
      .onServer()
      .named(ProviderConstants.PARTITION_MANAGEMENT_CREATE_PARTITION)
      .withParameter(Parameters.class, ProviderConstants.PARTITION_MANAGEMENT_PARTITION_ID, new IntegerType(2))
      .andParameter(ProviderConstants.PARTITION_MANAGEMENT_PARTITION_NAME, new CodeType("TENANT-B"))
      .execute();


    ourClientTenantInterceptor.setTenantId("TENANT-B");
    Patient pt = new Patient();
    pt.addName().setFamily("Family B");
    ourClient.create().resource(pt).execute().getId();

    Bundle searchResult = ourClient.search().forResource(Patient.class).returnBundle(Bundle.class).cacheControl(new CacheControlDirective().setNoCache(true)).execute();
    assertEquals(1, searchResult.getEntry().size());
    Patient pt2 = (Patient) searchResult.getEntry().get(0).getResource();
    assertEquals("Family B", pt2.getName().get(0).getFamily());
  }

  @AfterClass
  public static void afterClass() throws Exception {
    ourServer.stop();
  }

  @BeforeClass
  public static void beforeClass() throws Exception {
    String path = Paths.get("").toAbsolutePath().toString();

    ourLog.info("Project base path is: {}", path);

    ourServer = new Server(0);

    WebAppContext webAppContext = new WebAppContext();
    webAppContext.setContextPath("/hapi-fhir-jpaserver");
    webAppContext.setDisplayName("HAPI FHIR");
    webAppContext.setDescriptor(path + "/src/main/webapp/WEB-INF/web.xml");
    webAppContext.setResourceBase(path + "/target/hapi-fhir-jpaserver-starter");
    webAppContext.setParentLoaderPriority(true);

    ourServer.setHandler(webAppContext);
    ourServer.start();

    ourPort = JettyUtil.getPortForStartedServer(ourServer);

    ourCtx.getRestfulClientFactory().setServerValidationMode(ServerValidationModeEnum.NEVER);
    ourCtx.getRestfulClientFactory().setSocketTimeout(1200 * 1000);
    String ourServerBase = HapiProperties.getServerAddress();
    ourServerBase = "http://localhost:" + ourPort + "/hapi-fhir-jpaserver/fhir/";

    ourClient = ourCtx.newRestfulGenericClient(ourServerBase);
    ourClient.registerInterceptor(new LoggingInterceptor(true));

    ourClientTenantInterceptor = new UrlTenantSelectionInterceptor();
    ourClient.registerInterceptor(ourClientTenantInterceptor);
  }

  public static void main(String[] theArgs) throws Exception {
    ourPort = 8080;
    beforeClass();
  }
}