#!/bin/bash
#
# Copyright contributors to the Hyperledger Fabric Operator project
#
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
#
# 	  http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

  # todo: Refuse to overwrite an existing admin enrollment ?


function channel_command_group() {
  # set -x

  COMMAND=$1
  shift

  if [ "${COMMAND}" == "create" ]; then
    log "Creating channel \"${CHANNEL_NAME}\":"
    channel_up
    log "🏁 - Channel is ready."

  else
    print_help
    exit 1
  fi
}

function channel_up() {
  set -x

  enroll_org_admins

  create_channel_msp
  create_genesis_block

  join_channel_orderers
  join_channel_peers
}

# create an enrollment MSP config.yaml
function create_msp_config_yaml() {
  local ca_name=$1
  local ca_cert_name=$2
  local msp_dir=$3
  echo "Creating msp config ${msp_dir}/config.yaml with cert ${ca_cert_name}"

  cat << EOF > ${msp_dir}/config.yaml
NodeOUs:
  Enable: true
  ClientOUIdentifier:
    Certificate: cacerts/${ca_cert_name}
    OrganizationalUnitIdentifier: client
  PeerOUIdentifier:
    Certificate: cacerts/${ca_cert_name}
    OrganizationalUnitIdentifier: peer
  AdminOUIdentifier:
    Certificate: cacerts/${ca_cert_name}
    OrganizationalUnitIdentifier: admin
  OrdererOUIdentifier:
    Certificate: cacerts/${ca_cert_name}
    OrganizationalUnitIdentifier: orderer
EOF
}

function get_connection_profile() {
  local node_name=$1
  local connection_profile=$2

  mkdir -p $(dirname ${connection_profile})

  echo "writing $node_name connection profile to $connection_profile"

  kubectl -n $NS get cm/${node_name}-connection-profile -o json \
    | jq -r .binaryData.\"profile.json\" \
    | base64 -d \
    > ${connection_profile}
}

function enroll_org_admin() {
  local type=$1
  local org=$2
  local username=$3
  local password=$4

  echo "Enrolling $type org admin $username"

  ENROLLMENTS_DIR=${TEMP_DIR}/enrollments
  ORG_ADMIN_DIR=${ENROLLMENTS_DIR}/${org}/users/${username}

  # skip the enrollment if the admin certificate is available.
  if [ -f "${ORG_ADMIN_DIR}/msp/keystore/key.pem" ]; then
    echo "Found an existing admin enrollment at ${ORG_ADMIN_DIR}"
    return
  fi

  # Retrieve the CA information from Kubernetes
  CA_NAME=${org}-ca
  CA_DIR=${TEMP_DIR}/cas/${CA_NAME}
  CONNECTION_PROFILE=${CA_DIR}/connection-profile.json

  get_connection_profile $CA_NAME $CONNECTION_PROFILE

  # extract the CA enrollment URL and tls cert from the org connection profile
  CA_AUTH=${username}:${password}
  CA_ENDPOINT=$(jq -r .endpoints.api $CONNECTION_PROFILE)
  CA_HOST=$(echo ${CA_ENDPOINT} | cut -d/ -f3 | tr ':' '\n' | head -1)
  CA_PORT=$(echo ${CA_ENDPOINT} | cut -d/ -f3 | tr ':' '\n' | tail -1)
  CA_URL=https://${CA_AUTH}@${CA_HOST}:${CA_PORT}

  jq -r .tls.cert $CONNECTION_PROFILE | base64 -d >& $CA_DIR/tls-cert.pem

  # enroll the admin user
  FABRIC_CA_CLIENT_HOME=${ORG_ADMIN_DIR} fabric-ca-client enroll --url ${CA_URL} --tls.certfiles ${CA_DIR}/tls-cert.pem

  # Construct an msp config.yaml
  CA_CERT_NAME=${NS}-${CA_NAME}-ca-$(echo $INGRESS_DOMAIN | tr -s . -)-${CA_PORT}.pem

  create_msp_config_yaml ${CA_NAME} ${CA_CERT_NAME} ${ORG_ADMIN_DIR}/msp

  # private keys are hashed by name, but we only support one enrollment.
  # test-network examples refer to this as "server.key", which is incorrect.
  # This is the private key used to endorse transactions using the admin's
  # public key.
  mv ${ORG_ADMIN_DIR}/msp/keystore/*_sk ${ORG_ADMIN_DIR}/msp/keystore/key.pem


  # enroll the admin user at the TLS CA - used for the channel admin API
  FABRIC_CA_CLIENT_HOME=${ORG_ADMIN_DIR} \
    fabric-ca-client enroll \
    --url ${CA_URL} \
    --tls.certfiles ${CA_DIR}/tls-cert.pem \
    --mspdir ${ORG_ADMIN_DIR}/tls \
    --caname tlsca

  mv ${ORG_ADMIN_DIR}/tls/keystore/*_sk ${ORG_ADMIN_DIR}/tls/keystore/key.pem
}

function enroll_org_admins() {
  push_fn "Enrolling org admin users"

  enroll_org_admin orderer org0 org0admin org0adminpw
  enroll_org_admin peer    org1 org1admin org1adminpw
  enroll_org_admin peer    org2 org2admin org2adminpw

  pop_fn
}

function create_channel_org_msp() {
  local type=$1
  local org=$2
  echo "Creating channel org $org MSP"

  CA_DIR=${TEMP_DIR}/cas/${org}-ca
  ORG_MSP_DIR=${TEMP_DIR}/channel-msp/${type}Organizations/${org}/msp

  mkdir -p ${ORG_MSP_DIR}/cacerts
  mkdir -p ${ORG_MSP_DIR}/tlscacerts

  jq -r .ca.signcerts ${CA_DIR}/connection-profile.json | base64 -d >& ${ORG_MSP_DIR}/cacerts/ca-signcert.pem
  jq -r .tlsca.signcerts ${CA_DIR}/connection-profile.json | base64 -d >& ${ORG_MSP_DIR}/tlscacerts/tlsca-signcert.pem

  create_msp_config_yaml ${org}-ca ca-signcert.pem ${ORG_MSP_DIR}
}

function create_channel_msp() {
  push_fn "Creating channel MSP"

  create_channel_org_msp orderer org0
  create_channel_org_msp peer org1
  create_channel_org_msp peer org2

  extract_orderer_tls_cert org0 orderersnode1
  extract_orderer_tls_cert org0 orderersnode2
  extract_orderer_tls_cert org0 orderersnode3

  pop_fn
}

function extract_orderer_tls_cert() {
  local org=$1
  local orderer=$2

  echo "Extracting TLS cert for $org $orderer"

  ORDERER_NAME=${org}-${orderer}
  ORDERER_DIR=${TEMP_DIR}/channel-msp/ordererOrganizations/${org}/orderers/${ORDERER_NAME}
  ORDERER_TLS_DIR=${ORDERER_DIR}/tls
  CONNECTION_PROFILE=${ORDERER_DIR}/connection-profile.json

  get_connection_profile $ORDERER_NAME $CONNECTION_PROFILE

  mkdir -p $ORDERER_TLS_DIR/signcerts

  jq -r .tls.signcerts ${CONNECTION_PROFILE} \
    | base64 -d \
    >& $ORDERER_TLS_DIR/signcerts/tls-cert.pem
}

function create_genesis_block() {
  push_fn "Creating channel genesis block"

  mkdir -p ${TEMP_DIR}/config
  cp ${PWD}/config/core.yaml ${TEMP_DIR}/config/

  # The channel configtx file needs to specify dynamic elements from the environment,
  # for instance, the ${INGRESS_DOMAIN} for ingress controller and service endpoints.
  cat ${PWD}/config/configtx-template.yaml | envsubst > ${TEMP_DIR}/config/configtx.yaml

  FABRIC_CFG_PATH=${TEMP_DIR}/config \
    configtxgen \
      -profile      TwoOrgsApplicationGenesis \
      -channelID    $CHANNEL_NAME \
      -outputBlock  ${TEMP_DIR}/genesis_block.pb

#  configtxgen -inspectBlock ${TEMP_DIR}/genesis_block.pb

  pop_fn
}

function join_channel_orderers() {
  push_fn "Joining orderers to channel ${CHANNEL_NAME}"

  join_channel_orderer org0 orderersnode1
  join_channel_orderer org0 orderersnode2
  join_channel_orderer org0 orderersnode3

  # todo: readiness / liveiness equivalent for channel?  Needs a little bit to settle before peers can join.
  sleep 10

  pop_fn
}

# Request from the channel ADMIN api that the orderer joins the target channel
function join_channel_orderer() {
  local org=$1
  local orderer=$2

  # The client certificate presented in this case is the admin USER TLS enrollment key.  This is a stronger assertion
  # of identity than the Docker Compose network, which transmits the orderer NODE TLS key pair directly

  osnadmin channel join \
    --orderer-address ${NS}-${org}-${orderer}-admin.${INGRESS_DOMAIN} \
    --ca-file         ${TEMP_DIR}/channel-msp/ordererOrganizations/${org}/orderers/${org}-${orderer}/tls/signcerts/tls-cert.pem \
    --client-cert     ${TEMP_DIR}/enrollments/${org}/users/${org}admin/tls/signcerts/cert.pem \
    --client-key      ${TEMP_DIR}/enrollments/${org}/users/${org}admin/tls/keystore/key.pem \
    --channelID       ${CHANNEL_NAME} \
    --config-block    ${TEMP_DIR}/genesis_block.pb
}

function join_channel_peers() {
  join_org_peers 1
  join_org_peers 2
}

function join_org_peers() {
  local orgnum=$1
  push_fn "Joining org${orgnum} peers to channel ${CHANNEL_NAME}"

  # Join peers to channel
  join_channel_peer $orgnum 1
  #join_channel_peer $orgnum 2

  pop_fn
}

function join_channel_peer() {
  local orgnum=$1
  local peernum=$2

  export_peer_context $orgnum $peernum

  peer channel join \
    --blockpath   ${TEMP_DIR}/genesis_block.pb
}
