/*
 * ******************************************************
 * Copyright 2016-2023 VMware, Inc.  All Rights Reserved.
 * ******************************************************
 */

package com.vmware.vsan.connection;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import jakarta.xml.ws.BindingProvider;
import jakarta.xml.ws.handler.MessageContext;

import com.vmware.connection.BasicConnection;
import com.vmware.connection.Connection;
import com.vmware.connection.ConnectionException;
import com.vmware.connection.ConnectionMalformedUrlException;
import com.vmware.pbm.PbmPortType;
import com.vmware.pbm.PbmService;
import com.vmware.pbm.PbmServiceInstanceContent;
import com.vmware.vim25.InvalidLoginFaultMsg;
import com.vmware.vim25.InvalidPropertyFaultMsg;
import com.vmware.vsan.sdk.InvalidLocaleFaultMsg;
import com.vmware.vsan.sdk.ManagedObjectReference;
import com.vmware.vsan.sdk.RuntimeFaultFaultMsg;
import com.vmware.vsan.util.VsanUtil;

/**
 * This is the SPBM connection extends the basic vCenter server
 * connection class to provide SPBM service connection service.
 *
 * SPBM utilizes the per-authenticated HTTP session cookie from the
 * vCenter server for its authentication and sets the PbmPortType to use
 * the same session to make further health API calls.
 *
 * @see BasicConnection
 */
public class SpbmConnection extends BasicConnection {

   private static final String PBMSERVICEINSTANCETYPE = "PbmServiceInstance";
   private static final String PBMSERVICEINSTANCEVALUE = "ServiceInstance";

   private ManagedObjectReference pbmSvcInstRef;
   private URL spbmurl;
   private PbmService pbmService = new PbmService();
   private PbmPortType pbmPort = pbmService.getPbmPort();
   private PbmServiceInstanceContent pbmServiceContent;

   @SuppressWarnings({ "unchecked" })
   private void _connectToPbmService()
         throws RuntimeFaultFaultMsg, InvalidLocaleFaultMsg,
         InvalidLoginFaultMsg, com.vmware.pbm.RuntimeFaultFaultMsg,
         InvalidPropertyFaultMsg {
      System.out.println("Initiating connection to PBM Service...");
      System.out.println("===========================================");

      Map vimServiceCtxHeaders = super.getHeaders();
      Map pbmHeaders = new HashMap();
      pbmHeaders.put("Cookie", vimServiceCtxHeaders.get("Set-Cookie"));
      setSpbmURL();

      pbmService = new PbmService();
      pbmPort = pbmService.getPbmPort();

      Map<String, Object> pbmCtxt =
            ((BindingProvider) pbmPort).getRequestContext();
      pbmCtxt.put(BindingProvider.SESSION_MAINTAIN_PROPERTY, true);
      pbmCtxt
            .put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, spbmurl.toString());
      pbmCtxt.put(MessageContext.HTTP_REQUEST_HEADERS, pbmHeaders);

      com.vmware.vim25.ManagedObjectReference pbmSvcVcInstRef =
            VsanUtil.ConvertToVcMoRef(getPbmServiceInstanceReference());
      pbmServiceContent = pbmPort.pbmRetrieveServiceContent(pbmSvcVcInstRef);
      System.out.println("PBM Connection established...");
      System.out.println("============================================\n");
   }

   @Override
   public Connection connect() {
      if (!isConnected()) {
         super.connect();
         try {
            _connectToPbmService();
         } catch (Exception e) {
            Throwable cause = (e.getCause() != null) ? e.getCause() : e;
            throw new ConnectionException("failed to connect: "
                  + e.getMessage() + " : " + cause.getMessage(), cause);
         }
      }
      return this;
   }

   @Override
   public Connection disconnect() {
      if (this.isConnected()) {
         try {
            return super.disconnect();
         } finally {
            pbmPort = null;
            pbmService = null;
         }
      }
      return this;
   }

   public PbmPortType getPbmPort() {
      return pbmPort;
   }

   public PbmService getPbmService() {
      return pbmService;
   }

   public PbmServiceInstanceContent getPbmServiceContent() {
      return pbmServiceContent;
   }

   public ManagedObjectReference getPbmServiceInstanceReference() {
      if (pbmSvcInstRef == null) {
         ManagedObjectReference ref = new ManagedObjectReference();
         ref.setType(PBMSERVICEINSTANCETYPE);// "PbmServiceInstance"
         ref.setValue(PBMSERVICEINSTANCEVALUE);
         pbmSvcInstRef = ref;
      }
      return pbmSvcInstRef;
   }

   public URL setSpbmURL() {
      if (this.spbmurl == null) {
         try {
            this.spbmurl =
                  new URL(getUrl().toString().replace("/sdk", "/pbm"));
         } catch (MalformedURLException e) {
            throw new ConnectionMalformedUrlException(
                  "malformed URL argument: '" + spbmurl + "'", e);
         }
      }
      return this.spbmurl;
   }
}
