<?php


namespace App\Controllers;

class Provision extends BaseController
{
    private function testSocket(): void
    {
        // TELL THE SOCKET TO REFRESH PAGE
        $data["product_uid"] = "bea86662-adb3-4c09-816b-014f450d9713";
        $data["provision_action"] = "Auto%20Configuration%20Started";
        $SOCKET_SERVER_LOCATION = $_ENV['SOCKET_SERVER_LOCATION'];
        log_message('critical', "TEST TEST ***** ***** Provision :: Action To :: " . $SOCKET_SERVER_LOCATION . '/broadcast/provisioning');
        $this->APIcall("GET", $SOCKET_SERVER_LOCATION . '/broadcast/provisioning', $data);
    }

    public function prepareProvision(): string
    {

        //$this->testSocket();
        $current_database = $_ENV['MAIN_DATABASE_IP'];
        log_message('critical', "***** ***** prepareProvision  :: Working Database " . $current_database);

        log_message('critical', "***** ***** prepareProvision  :: Starting Port Allocation ");
        $this->allocatePortNo(); //  allocte port for the conyainer use

        log_message('critical', "***** ***** prepareProvision  :: Starting Product Database if Needed ");
        $this->prepareDataBase(); //  allocte port for the conyainer use

        log_message('critical', "***** ***** prepareProvision  :: Create needed DockerCompose Files ");
        $this->updateProvision();

        log_message('critical', "***** ***** prepareProvision  :: Populate HAPROXY for URL ");
        $this->configureURL();

        log_message('critical', "***** ***** scheduleRefreshAction  :: Refresh Requested Sites  ");
        $this->scheduleRefreshAction();

        return 0;
    }

//PROVISION_MSQL_DATABASE_IP="172.16.4.96"
//PROVISION_MSQL_DATABASE_USERNAME="root"
//PROVISION_MSQL_DATABASE_PASSWORD="may12002!"
//PROVISION_MSQL_DATABASE="wordpress"
//PROVISION_MSQL_DATABASE_PORT="3307"

    private function prepareDataBase(): string
    {
        $servername = $_ENV['PROVISION_MSQL_DATABASE_IP'] . ":" . $_ENV['PROVISION_MSQL_DATABASE_PORT']; //   "172.16.4.96:3307";
        $database = $_ENV['PROVISION_MSQL_DATABASE']; //"wordpress";
        $username = $_ENV['PROVISION_MSQL_DATABASE_USERNAME']; //"root";
        $password = $_ENV['PROVISION_MSQL_DATABASE_PASSWORD']; //"may12002!";
        // Create connection
        $conn = mysqli_connect($servername, $username, $password, $database);
        if ($conn->connect_error) {
            die("Connection failed: " . $conn->connect_error);
        }
        //echo "Connected successfully";
        log_message('critical', "***** ***** prepareDataBase   :: Connected successfully ");

        $list_limit = 2;
        $mysql = "SELECT id, member_id, uid, internal_url,product_id,status,provision_port,updated 
                  FROM members_products 
                  WHERE db_status = 0  
                  AND  product_id IN ('A000004','A000003')  
                  AND  provision_status = 0 
                  ORDER BY updated ASC LIMIT " . $list_limit;

        $query = $this->db->query($mysql);
        $provision_list = $query->getResult();
        foreach ($provision_list as $pr) {
            $memberID = $pr->member_id;
            $productId = $pr->product_id;
            $provisionUID = $pr->uid;
            $ContainerName = $this->generatedContainerName($productId, $pr->id); // $productId . str_pad($pr->id, 10, "0", STR_PAD_LEFT);

            $dbName = $ContainerName;
            // Create database
            $sql = "CREATE DATABASE $dbName";
            if ($conn->query($sql) === TRUE) {
                // echo "Database created successfully";
                log_message('critical', "***** ***** prepareDataBase :: () Database created successfully ");

                $mysql = "UPDATE members_products SET updated=now() WHERE uid::TEXT = '" . $provisionUID . "'";
                try {

                    $query = $this->db->query($mysql);

                    $sql = "CREATE USER '" . $dbName . "'@'localhost' IDENTIFIED BY '" . $provisionUID . "'";
                    $result = mysqli_query($conn, $sql);
                    $sql = "GRANT SELECT , INSERT , UPDATE , DELETE ON " . $dbName . " . * TO '" . $dbName . "'@'localhost' IDENTIFIED BY '" . $provisionUID . "'";
                    $result = mysqli_query($conn, $sql);

                    $mysql = "UPDATE members_products SET updated=now(), db_status = db_status + 1  WHERE uid::TEXT = '" . $provisionUID . "'";
                    $query = $this->db->query($mysql);
                } catch (\Exception $e) {
                    // echo 'Message: ' .$e->getMessage();
                    log_message('critical', "***** ***** prepareDataBase Error:: () " . $e->getMessage());
                }

            } else {
                //echo "Error creating database: " . $conn->error;
                log_message('critical', "***** ***** prepareDataBase Error:: (Error creating database:) " . $conn->error);
            }
        }
        mysqli_close($conn);
        return '';
    }

    private function generatedContainerName($productId, $provID)
    {
        return $productId . "-SVR-" . str_pad($provID, 10, "0", STR_PAD_LEFT);
    }

    public function updateProvision(): string
    {
        $list_limit = 1; // for noow
        $public_path = FCPATH;
        $ansible_folder = str_replace("public/", "ANSIBLE", $public_path);
        //$partMachineLocal = "/home/chiefsoft/MermsProvision/ANSIBLE/parts/";
        $partMachineLocal = $_ENV['PATH_MACHINE_LOCAL']; // "/home/chiefsoft/MermsProvision/ANSIBLE/parts/";
        if (!is_dir($ansible_folder . "/parts")) {
            mkdir($ansible_folder . "/parts", 0700);
        }

        $mysql = "SELECT id, uid, internal_url,product_id,status,provision_port,updated, member_id, 
                  id AS subscription_id
                  FROM members_products 
                  WHERE provision_port > 0 
                  AND provision_status = 0  AND p_file = 0 
                  ORDER BY updated ASC LIMIT " . $list_limit;

        $query = $this->db->query($mysql);
        // $num = $query->num_rows();
        $provision_list = $query->getResult();
        //$provision_list =[];
        foreach ($provision_list as $pr) {
            $subscriptionId = $pr->subscription_id;
            $provisionPort = $pr->provision_port;
            $productId = $pr->product_id;
            $provisionUID = $pr->uid;
            $memberID = $pr->member_id;
            $ContainerName = $this->generatedContainerName($productId, $pr->id); // $productId . str_pad($pr->id, 10, "0", STR_PAD_LEFT);
            $partFolder = $ansible_folder . "/parts/" . $provisionUID;

//            // make sure we compose the docker compose to use
//            $this->prepareDockerComposer(
//                $subscriptionId,
//                $provisionPort,
//                $productId,
//                $provisionUID,
//                $memberID,
//                $pr->internal_url
//            );


            $allocatedPort = $provisionPort;
            $this->updateToNow($provisionUID);

            if (!is_dir($partFolder)) {
                mkdir($partFolder, 0700);
            }

            // THIS PROVISONING COMPOSERS
            $composer_template_path = $ansible_folder . "/templates/composers/" . $productId . "/docker-compose.yml";
            $composer_template_path_local = $partMachineLocal . $provisionUID;

            $prov_name = str_replace(".", "_", $pr->internal_url) . ".yml";
            $template_file = $ansible_folder . "/templates/" . $pr->product_id . ".yml";
            //Get the domposer template file
            $destinationHomeFolder = $_ENV['DESTINATION_HOME_FOLDER'] . $ContainerName;
            $composer_template_contents = file_get_contents($composer_template_path);
            // Do the processing
            // $composer_template_contents = str_replace("WHAT_CONTAINER_NAME", $ContainerName, $composer_template_contents);
            //$destinationHomeFolder = "/home/chiefsoft/SITES/" . $ContainerName;
//            $SITE_CONTENTS_GATEWAY = $_ENV['SITE_CONTENTS_GATEWAY'];
//            $SITE_CONTACT_GATEWAY = $_ENV['SITE_CONTACT_GATEWAY'];
//            $MYSQL_PROVISION_DATABASE_PORT = $_ENV['PROVISION_MSQL_DATABASE_IP'] . ":" . $_ENV['PROVISION_MSQL_DATABASE_PORT']; //   "172.16.4.96:3307";
//            $destinationHomeFolder = $_ENV['DESTINATION_HOME_FOLDER'] . $ContainerName;
//            $composer_template_contents = str_replace("DESTINATION_FOLDER", $destinationHomeFolder, $composer_template_contents);
//            $composer_template_contents = str_replace("ALLOCATED_PORT", $allocatedPort, $composer_template_contents);
//            $composer_template_contents = str_replace("CNT_DB_NAME", $ContainerName, $composer_template_contents);
//            $composer_template_contents = str_replace("CNT_DB_PASS", $provisionUID, $composer_template_contents);
//            $composer_template_contents = str_replace("CNT_DB_DATA", $MYSQL_PROVISION_DATABASE_PORT, $composer_template_contents);
//            $composer_template_contents = str_replace("PROVISION_UID_REPLACE", $provisionUID, $composer_template_contents);
//            $composer_template_contents = str_replace("PROVISION_IMAGE_NAME_REPLACE", "registry.chiefsoft.com/pweb/pweb-01-merms-pweb0006c:latest", $composer_template_contents);
//            $composer_template_contents = str_replace("SITE_CONTENTS_URL_REPLACE", $SITE_CONTENTS_GATEWAY, $composer_template_contents);
//            $composer_template_contents = str_replace("SITE_CONTACT_URL_REPLACE", $SITE_CONTACT_GATEWAY, $composer_template_contents);
//
//            $templateRandom = 'P' . rand(1, 6);
//            $composer_template_contents = str_replace("TEMPLATE_NAME_REPLACE", $templateRandom, $composer_template_contents);

            $composer_template_contents = $this->prepareTemplateFile($composer_template_contents, $ContainerName, $allocatedPort, $provisionUID, $ansible_folder, $productId, $pr->internal_url);

            //Save to the specific path
//            $destination_composer_path = $ansible_folder . "/parts/" . $provisionUID . "/docker-compose.yml";
//            $composerFile = fopen($destination_composer_path, "w") or die("Unable to open file!");
//            fwrite($composerFile, $composer_template_contents);
//            fclose($composerFile);

            // GET THE TEMPLATE FOR THIS PROVISIONING
            $template_content = file_get_contents($template_file);
            $template_content_processed = str_replace("WHAT_HOST_IN_USE", $productId . "_SERVER", $template_content);
            $template_content_processed = str_replace("WHAT_PART_FOLDER", $partFolder, $template_content_processed);
            $template_content_processed = str_replace("DESTINATION_FOLDER", $destinationHomeFolder, $template_content_processed);
            $template_content_processed = str_replace("WHAT_PART_LOCAL", $composer_template_path_local, $template_content_processed); // depends on the local machine
            // CREATE PROVISOINING FILE
            $myfile = fopen($ansible_folder . "/" . $prov_name, "w") or die("Unable to open file!");
            fwrite($myfile, $template_content_processed);
            fclose($myfile);


            $this->registerProvision($subscriptionId, $prov_name);
            $this->provisionActions($memberID, $provisionUID, "Auto Configuration Started");
        }
        return 0;
    }

    private function prepareTemplateFile($composer_template_contents, $ContainerName, $allocatedPort, $provisionUID, $ansible_folder, $productId, $internalUrl): string
    {
        $PROVISION_IMAGE_NAME = match ($productId) {
            "A000001" => "registry.chiefsoft.com/pweb/pweb-01-merms-pweb0006c:latest",
            "A000002" => "registry.chiefsoft.com/bweb/bweb-01-merms-pweb0006c:latest",
            default => '',
        };
        $partMachineLocal = $_ENV['PATH_MACHINE_LOCAL'];
        $SITE_CONTENTS_GATEWAY = $_ENV['SITE_CONTENTS_GATEWAY'];
        $SITE_CONTACT_GATEWAY = $_ENV['SITE_CONTACT_GATEWAY'];
        $MYSQL_PROVISION_DATABASE_PORT = $_ENV['PROVISION_MSQL_DATABASE_IP'] . ":" . $_ENV['PROVISION_MSQL_DATABASE_PORT']; //   "172.16.4.96:3307";
        $destinationHomeFolder = $_ENV['DESTINATION_HOME_FOLDER'] . $ContainerName;
        $templateRandom = 'P' . rand(1, 6);
        $composer_template_contents = str_replace("WHAT_CONTAINER_NAME", $ContainerName, $composer_template_contents);
        $composer_template_contents = str_replace("DESTINATION_FOLDER", $destinationHomeFolder, $composer_template_contents);
        $composer_template_contents = str_replace("ALLOCATED_PORT", $allocatedPort, $composer_template_contents);
        $composer_template_contents = str_replace("CNT_DB_NAME", $ContainerName, $composer_template_contents);
        $composer_template_contents = str_replace("CNT_DB_PASS", $provisionUID, $composer_template_contents);
        $composer_template_contents = str_replace("CNT_DB_DATA", $MYSQL_PROVISION_DATABASE_PORT, $composer_template_contents);
        $composer_template_contents = str_replace("PROVISION_UID_REPLACE", $provisionUID, $composer_template_contents);
        $composer_template_contents = str_replace("PROVISION_IMAGE_NAME_REPLACE", $PROVISION_IMAGE_NAME, $composer_template_contents);
        $composer_template_contents = str_replace("SITE_CONTENTS_URL_REPLACE", $SITE_CONTENTS_GATEWAY, $composer_template_contents);
        $composer_template_contents = str_replace("SITE_CONTACT_URL_REPLACE", $SITE_CONTACT_GATEWAY, $composer_template_contents);
        $composer_template_contents = str_replace("TEMPLATE_NAME_REPLACE", $templateRandom, $composer_template_contents);

        try {
            //Save to the specific path
            $destination_composer_path = $ansible_folder . "/parts/" . $provisionUID . "/docker-compose.yml";
            $composerFile = fopen($destination_composer_path, "w") or die("Unable to open file!");
            fwrite($composerFile, $composer_template_contents);
            fclose($composerFile);
        } catch (\Exception $e) {
            log_message('critical', "***** ***** prepareTemplateFile Error:: () " . $e->getMessage());
        }

        $composer_template_path = $ansible_folder . "/templates/composers/" . $productId . "/docker-compose.yml";
        $composer_template_path_local = $partMachineLocal . $provisionUID;
        $prov_name = str_replace(".", "_", $internalUrl) . ".yml";
        $template_file = $ansible_folder . "/templates/" . $productId . ".yml";
        $partFolder = $ansible_folder . "/parts/" . $provisionUID;

        if (!is_dir($partFolder)) {
            mkdir($partFolder, 0700);
        }

        try {
            $template_content = file_get_contents($template_file);
            $template_content_processed = str_replace("WHAT_HOST_IN_USE", $productId . "_SERVER", $template_content);
            $template_content_processed = str_replace("WHAT_PART_FOLDER", $partFolder, $template_content_processed);
            $template_content_processed = str_replace("DESTINATION_FOLDER", $destinationHomeFolder, $template_content_processed);
            $template_content_processed = str_replace("WHAT_PART_LOCAL", $composer_template_path_local, $template_content_processed); // depends on the local machine
            // CREATE PROVISIONING FILE
            $myfile = fopen($ansible_folder . "/" . $prov_name, "w") or die("Unable to open file!");
            fwrite($myfile, $template_content_processed);
            fclose($myfile);
        } catch (\Exception $e) {
            log_message('critical', "***** ***** prepareTemplateFile Error:: () " . $e->getMessage());
        }
        // GET THE TEMPLATE FOR THIS PROVISIONING


        return $composer_template_contents;
    }

    private function scheduleRefreshAction(): void
    {
        $mysql = "SELECT  * FROM  member_product_refresh WHERE product_id IN ('A000001','A000002') AND status =0 LIMIT 1";
        $query = $this->db->query($mysql);
        // $num = $query->num_rows();
        $provision_list = $query->getResult();
        //$provision_list =[];
        foreach ($provision_list as $pr) {
            $this->db->query("UPDATE member_product_refresh  SET status = status + 1 WHERE id = " . $pr->id);
            $subscriptionUId = $pr->subscription_uid;
            if ($this->refreshProvision($subscriptionUId) == 100) {
                log_message('critical', "***** ***** scheduleRefreshAction Started:: () ");
            }
        }
    }

    private function refreshProvision($subscriptionUId): int
    {
        log_message('critical', "***** ***** refreshProvision  :: Execute Refresh Provisioning() ");
        $rsyncPass = "******noNEDD****";
        //    $remote_composer_path = "chiefsoft@10.0.0.32:/home/chiefsoft/MermsProvision/ANSIBLE/parts/";
        //    $partMachineLocal = "/home/chiefsoft/MermsProvision/ANSIBLE/parts/";
        $remote_composer_path = $_ENV['REMOTE_COMPOSER_PATH']; // "chiefsoft@10.0.0.32:/home/chiefsoft/MermsProvision/ANSIBLE/parts/";
        $partMachineLocal = $_ENV['PATH_MACHINE_LOCAL']; // "/home/chiefsoft/MermsProvision/ANSIBLE/parts/";
        $ansibleToken = $this->ansibleLogin();
        $mysql = "SELECT pp.uid AS prov_uid, p.id AS mpid, 
                 p.product_id, pp.id AS plan_id, pp.uid , 
                 pp.provision_id, pp.play_file,
                 p.uid AS member_product_uid ,
                 p.member_id AS member_id ,
                 p.internal_url ,
                 p.provision_port,
                 p.provision_status 
                 FROM provision_plans pp 
                 LEFT JOIN members_products p ON p.id =pp.provision_id 
                 WHERE p.uid = '$subscriptionUId' ";

        $query = $this->db->query($mysql);
        $provision_list = $query->getResult();
        foreach ($provision_list as $pr) {
            $playFile = $pr->play_file;
            $planId = $pr->plan_id;
            $provisionId = $pr->provision_id;
            $productId = $pr->product_id;
            $productUid = $pr->member_product_uid;
            $playbook = $pr->product_id . "_repair.yml";
            $provisionUID = $pr->prov_uid;
            $member_id = $pr->member_id;
            $internal_url = $pr->internal_url;
            $provision_port = $pr->provision_port;
            $members_product_id = $pr->mpid;

            log_message('critical', "Members Product ID ***** " . $pr->mpid . "  Provision_Status == " . $pr->provision_status);
            $this->updateToNow($productUid); // increase count not to repeat

            $ansible_host = $productId . "_SERVER";
            $ContainerName = $this->generatedContainerName($productId, $pr->mpid); //$productId ."-SVR-". str_pad($pr->mpid, 10, "0", STR_PAD_LEFT);
            //$destinationHomeFolder =  "/home/chiefsoft/SITES/" . $ContainerName;
            $destinationHomeFolder = $_ENV['DESTINATION_HOME_FOLDER'] . $ContainerName;
//            $composer_template_path_local = $partMachineLocal . $provisionUID;
//            $composer_template_path_remote = $remote_composer_path . $provisionUID;

            $composer_template_path_local = $partMachineLocal . $productUid;
            $composer_template_path_remote = $remote_composer_path . $productUid;

//            $ansible_transport_path = $pr->provision_uid; // $ContainerName;
            $ansible_transport_path = $productUid; // $ContainerName;

            // let us make sure all we promise are availale
            //"WHAT_PART_LOCAL


            // Remember this is in a CONTAINER so the path follows
            $this_composer_part_location = "/var/www/html/ANSIBLE/parts/" . $productUid;
            if (!is_dir($this_composer_part_location)) {
                mkdir($this_composer_part_location, 0700);
            }

            // make sure we compose the docker compose to use
            $this->prepareDockerComposer(
                $members_product_id,
                $provision_port,
                $productId,
                $productUid,
                $member_id,
                $internal_url
            );

            $this->db->query("UPDATE provision_plans SET updated = now() WHERE id = $planId");
            // $this->db->query("UPDATE members_products SET  p_file = p_file + 1 WHERE id = $provisionId");
            $params = [
                "template_id" => 2,
                "debug" => false,
                "dry_run" => false,
                "playbook" => $playbook,
                "environment" => "{
                        \"WHAT_HOST_IN_USE\":\"$ansible_host\",
                        \"DESTINATION_FOLDER\":\"$destinationHomeFolder\",
                        \"WHAT_PART_LOCAL\":\"$composer_template_path_local\",
                        \"WHAT_PART_REMOTE\":\"$composer_template_path_remote\",
                        \"ANSIBLE_TRANSPORT_PATH\":\"$ansible_transport_path\",
                        \"RSYNC_PASSWORD\":\"$rsyncPass\",
                        \"CONTAINER_NAME\":\"$ContainerName\"
                 }"
            ];
            $ansibleReturn = $this->ansibleProvision($ansibleToken, $params);

            log_message('critical', "ANSIBLE RETURN ***** " . $ansibleReturn);
            if ($ansibleReturn === 200) {
                log_message('critical', "Calling  update_To_Completed ***** " . $ansibleReturn);
                $this->update_To_Completed($productUid);
            } else {
                log_message('critical', "Calling WHY PATH  update_To_Completed ***** " . $ansibleReturn);
                $this->update_To_Completed($productUid);
            }
        }
        return 100;
    }

    private function prepareDockerComposer(
        $members_product_id,
        $members_product_port,
        $product_id,
        $member_product_uid,
        $member_id,
        $internal_url
    ): void
    {


        $public_path = FCPATH;
        $ansible_folder = str_replace("public/", "ANSIBLE", $public_path);
        $partMachineLocal = $_ENV['PATH_MACHINE_LOCAL'];

        $subscriptionId = $members_product_id;
        $provisionPort = $members_product_port;
        $productId = $product_id;
        $provisionUID = $member_product_uid;
        $memberID = $member_id;
        $ContainerName = $this->generatedContainerName($productId, $members_product_id); // $productId . str_pad($pr->id, 10, "0", STR_PAD_LEFT);
        $partFolder = $ansible_folder . "/parts/" . $member_product_uid;

        /*
        * may be if the file is already there , we can turn back
        *
        */
        $destination_composer_path = $ansible_folder . "/parts/" . $provisionUID . "/docker-compose.yml";


        $allocatedPort = $provisionPort;
        $this->updateToNow($provisionUID);

        if (!is_dir($partFolder)) {
            mkdir($partFolder, 0700);
        }

        // THIS PROVISONING COMPOSERS
        $composer_template_path = $ansible_folder . "/templates/composers/" . $productId . "/docker-compose.yml";
        $composer_template_path_local = $partMachineLocal . $provisionUID;

        $prov_name = str_replace(".", "_", $internal_url) . ".yml";
        $template_file = $ansible_folder . "/templates/" . $productId . ".yml";
        //Get the domposer template file
        $destinationHomeFolder = $_ENV['DESTINATION_HOME_FOLDER'] . $ContainerName;
        $composer_template_contents = file_get_contents($composer_template_path);
        // Do the processing
//        $composer_template_contents = str_replace("WHAT_CONTAINER_NAME", $ContainerName, $composer_template_contents);
//        //$destinationHomeFolder = "/home/chiefsoft/SITES/" . $ContainerName;
//        $SITE_CONTENTS_GATEWAY = $_ENV['SITE_CONTENTS_GATEWAY'];
//        $SITE_CONTACT_GATEWAY = $_ENV['SITE_CONTACT_GATEWAY'];
//        $MYSQL_PROVISION_DATABASE_PORT = $_ENV['PROVISION_MSQL_DATABASE_IP'] . ":" . $_ENV['PROVISION_MSQL_DATABASE_PORT']; //   "172.16.4.96:3307";
//        $destinationHomeFolder = $_ENV['DESTINATION_HOME_FOLDER'] . $ContainerName;
//        $composer_template_contents = str_replace("DESTINATION_FOLDER", $destinationHomeFolder, $composer_template_contents);
//        $composer_template_contents = str_replace("ALLOCATED_PORT", $allocatedPort, $composer_template_contents);
//        $composer_template_contents = str_replace("CNT_DB_NAME", $ContainerName, $composer_template_contents);
//        $composer_template_contents = str_replace("CNT_DB_PASS", $provisionUID, $composer_template_contents);
//        $composer_template_contents = str_replace("CNT_DB_DATA", $MYSQL_PROVISION_DATABASE_PORT, $composer_template_contents);
//        $composer_template_contents = str_replace("PROVISION_UID_REPLACE", $provisionUID, $composer_template_contents);
//        $composer_template_contents = str_replace("PROVISION_IMAGE_NAME_REPLACE", "registry.chiefsoft.com/pweb/pweb-01-merms-pweb0006c:latest", $composer_template_contents);
//        $composer_template_contents = str_replace("SITE_CONTENTS_URL_REPLACE", $SITE_CONTENTS_GATEWAY, $composer_template_contents);
//        $composer_template_contents = str_replace("SITE_CONTACT_URL_REPLACE", $SITE_CONTACT_GATEWAY, $composer_template_contents);
//
//
//        $templateRandom = 'P' . rand(1, 6);
//        $composer_template_contents = str_replace("TEMPLATE_NAME_REPLACE", $templateRandom, $composer_template_contents);
//
        $composer_template_contents = $this->prepareTemplateFile($composer_template_contents, $ContainerName, $allocatedPort, $provisionUID, $ansible_folder, $productId, $internal_url);

        //Save to the specific path
//        $composerFile = fopen($destination_composer_path, "w") or die("Unable to open file!");
//        fwrite($composerFile, $composer_template_contents);
//        fclose($composerFile);

        // GET THE TEMPLATE FOR THIS PROVISIONING
        /*
        $template_content = file_get_contents($template_file);
        $template_content_processed = str_replace("WHAT_HOST_IN_USE", $productId . "_SERVER", $template_content);
        $template_content_processed = str_replace("WHAT_PART_FOLDER", $partFolder, $template_content_processed);
        $template_content_processed = str_replace("DESTINATION_FOLDER", $destinationHomeFolder, $template_content_processed);
        $template_content_processed = str_replace("WHAT_PART_LOCAL", $composer_template_path_local, $template_content_processed); // depends on the local machine
        // CREATE PROVISOINING FILE
        $myfile = fopen($ansible_folder . "/" . $prov_name, "w") or die("Unable to open file!");
        fwrite($myfile, $template_content_processed);
        fclose($myfile);
        */
    }

    private function registerProvision($subscriptionId, $prov_name)
    {

        /*
         CREATE TABLE provision_plans (
		id SERIAL,
		uid uuid DEFAULT  uuid_generate_v4(),
		provision_id INT REFERENCES members_products(id),
		play_file  VARCHAR(100)UNIQUE  NOT NULL,
		msg VARCHAR(100) ,
		added timestamp without time zone DEFAULT now(),
		updated timestamp without time zone DEFAULT now()
	);
 ALTER TABLE ONLY provision_plans
    ADD CONSTRAINT provision_plans_id_key UNIQUE (id);

         */
        try {
            $mysql = "INSERT INTO provision_plans (provision_id,play_file) VALUES($subscriptionId,'$prov_name')";
            $query = $this->db->query($mysql);
        } catch (\Exception $e) {
            log_message('critical', "***** ***** registerProvision Error:: registerProvision() " . $e->getMessage());
        }
        return 0;
    }

    private function allocatePortNo()
    {
        $list_limit = 2;
        $mysql = "SELECT id, member_id, uid, internal_url,product_id,status,provision_port,updated 
                  FROM members_products 
                  WHERE provision_port = 0 
                  ORDER BY updated ASC LIMIT " . $list_limit;

        $query = $this->db->query($mysql);
        $provision_list = $query->getResult();
        foreach ($provision_list as $pr) {
            $memberID = $pr->member_id;
            $productId = $pr->product_id;
            $provisionUID = $pr->uid;

            $allocated_port = $this->allocatePortNumber($productId);
            $primary_server = $this->provisionServer($productId);
            $mysql = "UPDATE members_products SET updated=now(), provision_port=" . $allocated_port . ", primary_server='" . $primary_server . "' WHERE uid::TEXT = '" . $provisionUID . "' AND provision_port = 0";
            $query = $this->db->query($mysql);

            $this->provisionActions($memberID, $provisionUID, "Allocating Provisioning Ports");
        }

        //Now let us try fix hanging stuffs
        $mysql = "SELECT id, member_id, uid, internal_url,product_id,status,provision_port,updated 
                  FROM members_products 
                  WHERE provision_port > 0 
                  AND status = 6 
                  AND added < now() - INTERVAL '5 minutes'
                  ORDER BY updated ASC LIMIT 1";

        $query = $this->db->query($mysql);
        $provision_list = $query->getResult();
        foreach ($provision_list as $pr) {
            $memberID = $pr->member_id;
            $productId = $pr->product_id;
            $provisionUID = $pr->uid;
            $mysql = "UPDATE members_products SET updated=now(), provision_status=0  WHERE uid::TEXT = '" . $provisionUID . "' AND provision_port > 0";
            $query = $this->db->query($mysql);
        }
        //SELECT * FROM members_products WHERE status = 6 AND added < now() - INTERVAL '5 minutes' LIMIT 1
    }

    private function allocatePortNumber($productId)
    {
        $portF = 0;
        try {
            $mysql = "SELECT max(provision_port) AS max_port FROM  members_products  WHERE product_id = '" . $productId . "'";
            $query = $this->db->query($mysql);
            $maxItem = $query->getResult()[0]->max_port;
            //var_dump($maxItem);
            if ($maxItem == 0) {
                switch ($productId) {
                    case "A000002":
                        $maxItem = 5500;
                        break;
                    case "A000004":
                        $maxItem = 8800;
                        break;
                    case "A000005":
                        $maxItem = 7700;
                        break;
                    case "A000001":
                        $maxItem = 4400;
                        break;
                    case "A000003":
                        $maxItem = 6600;
                        break;
                }
            } else {
                $maxItem = $maxItem + 1;
            }
            $portF = $maxItem;
        } catch (\Exception $e) {
            echo 'Caught exception: ', $e->getMessage(), "\n";
        }
        return $portF;
    }

    private function provisionServer($productId)
    {
        $primaryServer = "";
        switch ($productId) {
            case "A000001":
            case "A000002":
                $primaryServer = $_ENV['DEPLOYMENT_SERVER_1'];
                break;
            case "A000003":
            case "A000004":
                $primaryServer = $_ENV['DEPLOYMENT_SERVER_2'];
                break;
            case "A000005":
                $primaryServer = $_ENV['DEPLOYMENT_SERVER_3'];
                break;
        }
        return $primaryServer;
    }

    /*
     *
    [A000001_SERVER]
    172.16.4.92

    [A000002_SERVER]
    172.16.4.92

    [A000003_SERVER]
    172.16.4.91

    [A000004_SERVER]
    172.16.4.91

    [A000005_SERVER]
    172.16.4.95
     */
    private function updateToNow($provisionUID): void
    {
        try {
            $mysql = "UPDATE members_products SET updated=now(), 
                      provision_status=provision_status+1 
                      WHERE uid::TEXT = '" . $provisionUID . "'";

            $query = $this->db->query($mysql);
        } catch (Exception $e) {
            echo 'Caught exception: ', $e->getMessage(), "\n";
            log_message('critical', "***** ***** Provision Error:: updateToNow() " . $e->getMessage());
        }
    }

    private function provisionActions($memberID, $provisionUID, $actionText)
    {
        log_message('critical', "***** ***** Provision :: provisionActions($memberID, $provisionUID,$actionText) ");
        try {
            $mysql = "INSERT INTO provision_actions (member_id,product_uid,action) VALUES ($memberID,'$provisionUID', '$actionText')";
            log_message('critical', "***** ***** Provision :: provisionActions(QUERY) :: " . $mysql);
            $query = $this->db->query($mysql);

            // TELL THE SOCKET TO REFRESH PAGE
            $data["product_uid"] = $provisionUID;
            $data["provision_action"] = $actionText;

            $SOCKET_SERVER_LOCATION = $_ENV['SOCKET_SERVER_LOCATION'];
            log_message('critical', "***** ***** Provision :: Action To :: " . $SOCKET_SERVER_LOCATION . '/broadcast/provisioning');
            $this->APIcall("GET", $SOCKET_SERVER_LOCATION . '/broadcast/provisioning', $data);

        } catch (\Exception $e) {
            // echo 'Caught exception: ',  $e->getMessage(), "\n";
            log_message('critical', "***** ***** Provision Error:: provisionActions() " . $e->getMessage());
        }
        return 0;
    }

    public function releaseProvision()
    {
        log_message('critical', "***** ***** ReleaseProvision  :: Execute Actual Provisioning() ");
        $PROCESS_LIMIT = 2;
        $rsyncPass = "******noNEDD****";
        //    $remote_composer_path = "chiefsoft@10.0.0.32:/home/chiefsoft/MermsProvision/ANSIBLE/parts/";
        //    $partMachineLocal = "/home/chiefsoft/MermsProvision/ANSIBLE/parts/";
        $remote_composer_path = $_ENV['REMOTE_COMPOSER_PATH']; // "chiefsoft@10.0.0.32:/home/chiefsoft/MermsProvision/ANSIBLE/parts/";
        $partMachineLocal = $_ENV['PATH_MACHINE_LOCAL']; // "/home/chiefsoft/MermsProvision/ANSIBLE/parts/";
        $ansibleToken = $this->ansibleLogin();
        $mysql = "SELECT pp.uid AS prov_uid, p.id AS mpid, 
                 p.product_id, pp.id AS plan_id, pp.uid , 
                 pp.provision_id, pp.play_file,
                 p.uid AS member_product_uid ,
                 p.member_id AS member_id ,
                 p.internal_url ,
                 p.provision_port,
                 p.provision_status 
                 FROM provision_plans pp 
                 LEFT JOIN members_products p ON p.id =pp.provision_id 
                 WHERE p.provision_status = 1 
                 ORDER BY pp.updated ASC LIMIT " . $PROCESS_LIMIT;
        $query = $this->db->query($mysql);
        $provision_list = $query->getResult();
        foreach ($provision_list as $pr) {
            $playFile = $pr->play_file;
            $planId = $pr->plan_id;
            $provisionId = $pr->provision_id;
            $productId = $pr->product_id;
            $productUid = $pr->member_product_uid;
            $playbook = $pr->product_id . ".yml";
            $provisionUID = $pr->prov_uid;
            $member_id = $pr->member_id;
            $internal_url = $pr->internal_url;
            $provision_port = $pr->provision_port;
            $members_product_id = $pr->mpid;

            log_message('critical', "Members Product ID ***** " . $pr->mpid . "  Provision_Status == " . $pr->provision_status);
            $this->updateToNow($productUid); // increase count not to repeat

            $ansible_host = $productId . "_SERVER";
            $ContainerName = $this->generatedContainerName($productId, $pr->mpid); //$productId ."-SVR-". str_pad($pr->mpid, 10, "0", STR_PAD_LEFT);
            //$destinationHomeFolder =  "/home/chiefsoft/SITES/" . $ContainerName;
            $destinationHomeFolder = $_ENV['DESTINATION_HOME_FOLDER'] . $ContainerName;
//            $composer_template_path_local = $partMachineLocal . $provisionUID;
//            $composer_template_path_remote = $remote_composer_path . $provisionUID;

            $composer_template_path_local = $partMachineLocal . $productUid;
            $composer_template_path_remote = $remote_composer_path . $productUid;

//            $ansible_transport_path = $pr->provision_uid; // $ContainerName;
            $ansible_transport_path = $productUid; // $ContainerName;

            // let us make sure all we promise are availale
            //"WHAT_PART_LOCAL


            // Remember this is in a CONTAINER so the path follows
            $this_composer_part_location = "/var/www/html/ANSIBLE/parts/" . $productUid;
            if (!is_dir($this_composer_part_location)) {
                mkdir($this_composer_part_location, 0700);
            }

            // make sure we compose the docker compose to use
            $this->prepareDockerComposer(
                $members_product_id,
                $provision_port,
                $productId,
                $productUid,
                $member_id,
                $internal_url
            );

            $this->db->query("UPDATE provision_plans SET updated = now() WHERE id = $planId");
            $this->db->query("UPDATE members_products SET  p_file = p_file + 1 WHERE id = $provisionId");
            $params = [
                "template_id" => 2,
                "debug" => false,
                "dry_run" => false,
                "playbook" => $playbook,
                "environment" => "{
                        \"WHAT_HOST_IN_USE\":\"$ansible_host\",
                        \"DESTINATION_FOLDER\":\"$destinationHomeFolder\",
                        \"WHAT_PART_LOCAL\":\"$composer_template_path_local\",
                        \"WHAT_PART_REMOTE\":\"$composer_template_path_remote\",
                        \"ANSIBLE_TRANSPORT_PATH\":\"$ansible_transport_path\",
                        \"RSYNC_PASSWORD\":\"$rsyncPass\",
                        \"CONTAINER_NAME\":\"$ContainerName\"
                 }"
            ];
            $ansibleReturn = $this->ansibleProvision($ansibleToken, $params);

            log_message('critical', "ANSIBLE RETURN ***** " . $ansibleReturn);
            if ($ansibleReturn === 200) {
                log_message('critical', "Calling  update_To_Completed ***** " . $ansibleReturn);
                $this->update_To_Completed($productUid);
            } else {
                log_message('critical', "Calling WHY PATH  update_To_Completed ***** " . $ansibleReturn);
                $this->update_To_Completed($productUid);
            }
        }
        return 0;
    }

    private function update_To_Completed($provisionUID)
    {
        log_message('critical', "update_To_Completed ********  ***** " . $provisionUID);
        try {
            $mysql = "UPDATE members_products SET updated=now(), 
                      provision_status=provision_status+1 ,
                      status = 7 
                      WHERE status = 6 
                      AND uid::TEXT = '" . $provisionUID . "'";

            $query = $this->db->query($mysql);
        } catch (Exception $e) {
            echo 'Caught exception: ', $e->getMessage(), "\n";
            log_message('critical', "***** ***** Provision Error:: updateToCompleted() " . $e->getMessage());
        }
    }

//    public function runAnsibleShell($provisionFile){
//        try{
//            log_message('critical', "***** ***** Provision :: runAnsibleShell($provisionFile) ");
//            $shellCommand ="/var/www/html/ANSIBLE/auto_play.sh A000001.yml OLU AMEYE "; ///var/www/html/ANSIBLE/$provisionFile";
//            log_message('critical', $shellCommand);
//            $output = shell_exec($shellCommand );
//            log_message('critical',  "***** ***** Provision :: runAnsibleShell(output) AFTER SHELL RUN ".serialize($output));
//
////            $shellCommand ="ansible-playbook   --vault-password-file /var/www/html/ANSIBLE/secrets.pass  --key-file ~/.ssh/ansible_worker -i /var/www/html/ANSIBLE/inventory  $provisionFile";
////            log_message('critical', $shellCommand);
////
////            $output = shell_exec($shellCommand );
////            $output = shell_exec($shellCommand );
////            log_message('critical',  "***** ***** Provision :: runAnsibleShell(output) AFTER SHELL RUN ".$output);
//        } catch ( \Exception $e){
//            log_message('critical', "***** ***** Provision Error:: runAnsibleShell() ".$e->getMessage());
//        }
//        return 0;
//    }

    public function directProvision()
    {

        $mysql = "SELECT id AS plan_id, uid, provision_id, play_file from provision_plans ORDER BY updated ASC LIMIT 1 ";
        $query = $this->db->query($mysql);
        $provision_list = $query->getResult();
        foreach ($provision_list as $pr) {
            $playFile = $pr->play_file;
            $planId = $pr->plan_id;
            $provisionId = $pr->provision_id;

            $this->db->query("UPDATE provision_plans SET updated = now() WHERE id = $planId");
            $this->db->query("UPDATE members_products SET  p_file = p_file + 1 WHERE id = $provisionId");
            //$this->runAnsibleShell($playFile);

            try {
                log_message('critical', "***** ***** Provision :: runAnsibleShell($playFile) ");
                $shellCommand = "ansible-playbook   --vault-password-file /var/www/html/ANSIBLE/secrets.pass  --key-file ~/.ssh/ansible_worker -i /var/www/html/ANSIBLE/inventory  /var/www/html/ANSIBLE/$playFile";
                log_message('critical', $shellCommand);
                $output = shell_exec($shellCommand);
                log_message('critical', "***** ***** Provision :: runAnsibleShell(output) AFTER SHELL RUN " . $output);
            } catch (\Exception $e) {
                log_message('critical', "***** ***** Provision Error:: runAnsibleShell() " . $e->getMessage());
            }
        }


        return 0;
    }


    public function demo(): string
    {

        $PROJECT_ID = 8;
        $result = "Unhandled exception";
        $base_url = "http://172.16.4.90:3000";
        $cookies = tempnam('/tmp', 'cookie.txt');

        # 1. Login
        $url = $base_url . "/api/auth/login";
        $ch = curl_init($url);
        # Setup request to send json via POST.
        $payload = json_encode(array("auth" => "admin", "password" => "may12002"));
        curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
        curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type:application/json'));
        # Return response instead of printing.
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        # Fetch the headers, not the body content:
        //curl_setopt($ch, CURLOPT_HEADER, true);  // we want headers
        //curl_setopt($ch, CURLOPT_NOBODY, true);  // we don't need body
        # Cookie jar
        curl_setopt($ch, CURLOPT_COOKIEJAR, $cookies);
        curl_setopt($ch, CURLOPT_COOKIEFILE, $cookies);

        # Send request.
        $result = curl_exec($ch);
        $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $result = 'HTTP code: ' . $httpcode;

        curl_close($ch);

        if ($httpcode != "204") {
            return "Login failed! $result";
        }

        # 2. Get token
        $url = $base_url . "/api/user/tokens";
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_COOKIEJAR, $cookies);
        curl_setopt($ch, CURLOPT_COOKIEFILE, $cookies);
        $res = curl_exec($ch);
        $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        $token = null;
        $arr = json_decode($res, true);
        foreach ($arr as $item) {
            if ($item["expired"] === false) {
                $token = $item["id"];
                break;
            }

        }
        $result = "Found token: $token";

        # 3. Lauch task
        $PROJECT_ID = 7;
        $url = $base_url . "/api/project/$PROJECT_ID/tasks";
        $headers = [
            'Content-Type:application/json',
            'Accept: application/json',
            'Authorization: Bearer ' . $token
        ];
        $ch = curl_init($url);
        $payload = json_encode(array("template_id" => 1, "debug" => false, "dry_run" => false, "playbook" => "first-playbook.yml", "environment" => "{}"));
        curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $res = curl_exec($ch);
        $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $result .= "\n" . 'Launch task HTTP code: ' . $httpcode;

        curl_close($ch);

        # 4. Get tasks
        $url = $base_url . "/api/project/$PROJECT_ID/tasks";
        $ch = curl_init($url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        $res = curl_exec($ch);
        $httpcode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        $result .= "\n" . 'Get tasks HTTP code: ' . $httpcode;

        curl_close($ch);

        ob_start();
        var_dump($headers);
        var_dump($res);
        $result .= "\n" . ob_get_clean();

        # Print response.
        return "<pre>$result</pre>";

    }

    public function configureURL()
    {
        log_message('critical', "***** ***** Configure URL    :: Set up URL access for product  ");
        $limit = 1;
        $mysql = "SELECT id, uid, internal_url,primary_server,provision_port,url_status FROM members_products WHERE url_status = 0 LIMIT $limit";
        $query = $this->db->query($mysql);
        $provision_list = $query->getResult();
        foreach ($provision_list as $pr) {
            $mbId = $pr->id;
            $provisionURL = $pr->internal_url;
            $provisionServer = $pr->primary_server;
            $provisionPort = $pr->provision_port;
            $provisionUID = $pr->uid;

            log_message('critical', "***** ***** Configure URL Start ::: $provisionURL == $provisionServer:$provisionPort  ");
            // Here  call haproxy actions
            list ($res, $message) = $this->configureHaproxyURL($provisionURL, $provisionServer, $provisionPort);
            // TODO: Check $res for errors
            $mysql_close = "UPDATE members_products  SET url_status = url_status + 1 WHERE id = $mbId ";
            $this->db->query($mysql_close);
            log_message('critical', "***** ***** Conclude if all Okay => $mysql_close  ");
        }

    }

    public function haproxy(): string
    {
//        $limit = 1;
//        $mysql = "SELECT
//                       internal_url,
//                       provision_port,
//                       product_id,
//                       url_status
//                  FROM members_products
//                  WHERE url_status = 0 LIMIT $limit";
//        $query = $this->db->query($mysql);
//        $provision_list = $query->getResult();
//        foreach ($provision_list as $pr) {
//            $provisionURL = $pr->internal_url;
//            $productId = $pr->product_id;
//            $provisionPort = $pr->provision_port;
//            $provisionUID = $pr->uid;
//
//
//
//            log_message('critical', "***** ***** Configure URL $provisionURL to Point to ::: $provisionServer:$provisionPort  ");
//
//        }


        list ($res, $message) = $this->configureHaproxyURL("23607.devprov.mermsemr.com", "172.16.4.91", "6603");
        var_dump($message);
        log_message('critical', "Message => '" . $message . "'");
        return "Message:<br/>" . $message;
    }

    public function configureHaproxyURL($provisionURL, $provisionServer, $provisionPort)
    {
        $message = "";
        $base_url = $_ENV['HAPROXY_SERVER_URL']; //'http://172.16.4.99:5555';
        $username = $_ENV['HAPROXY_SERVER_USER']; //'admin';
        $password = $_ENV['HAPROXY_SERVER_PASS']; //'Hapr0xy001';

        log_message('critical', "provisionURL => $provisionURL");
        log_message('critical', "provisionServer => $provisionServer");
        log_message('critical', "provisionPort => $provisionPort");

        $status_code = [];

        // 200
        list($status_code[0], $cfgver) = $this->getHaProxyCfgVer($base_url, $username, $password);
        $cfgver0 = trim($cfgver);

        // Add backend
        /*
# Add backend
# https://www.haproxy.com/documentation/dataplaneapi/community/?v=v3#post-/services/haproxy/configuration/backends
curl -XPOST --user admin:Hapr0xy001 \
-H 'Content-Type: application/json' -H 'Accept: application/json' \
-d '{
  "name": "backend'$CFGVER'",
  "mode": "http",
  "http_connection_mode": "httpclose",
  "forwardfor": {
    "enabled": "enabled"
  },
  "compression": {
    "algorithms":  ["gzip"],
    "types": ["text/html", "text/plain", "text/css", "text/javascript"]
  }
}' \
"http://172.16.4.99:5555/v3/services/haproxy/configuration/backends?version=$CFGVER" > backend.txt
        */
        $backend = "backend" . $cfgver0;
        $data = [
            "name" => $backend,
            "mode" => "http",
            "http_connection_mode" => "httpclose",
            "forwardfor" => [
                "enabled" => "enabled"
            ],
            "compression" => [
                "algorithms" => ["gzip"],
                "types" => ["text/html", "text/plain", "text/css", "text/javascript"]
            ]
        ];
        $uri = '/v3/services/haproxy/configuration/backends?version=' . $cfgver0;
        // 202
        list($status_code[1], $result) = $this->getHaProxyPost($base_url, $username, $password, $uri, $data);

        // TODO: check the status code

        // 200
        list($status_code[2], $cfgver) = $this->getHaProxyCfgVer($base_url, $username, $password);
        $cfgver = trim($cfgver);

        // Add Server
        /*
#https://www.haproxy.com/documentation/dataplaneapi/community/?v=v3#post-/services/haproxy/configuration/backends/-parent_name-/servers
curl -XPOST --user admin:Hapr0xy001 \
-H 'Content-Type: application/json' -H 'Accept: application/json' \
-d '{
  "name": "server'$CFGVER'",
  "address": "172.16.4.92",
  "port": 4401,
  "check": "enabled",
  "health_check_port": 4401
}' \
"http://172.16.4.99:5555/v3//services/haproxy/configuration/backends/$BACKEND/servers?version=$CFGVER" > server.txt
*/
# test => "server4"
        $server = "server" . $cfgver0;
        $data = [
            "name" => $server,
            "address" => $provisionServer,
            "port" => (int)$provisionPort,
            "check" => "enabled",
            "health_check_port" => (int)$provisionPort
        ];
        $uri = '/v3/services/haproxy/configuration/backends/' . $backend . '/servers?version=' . $cfgver;
        // 202
        list($status_code[3], $result) = $this->getHaProxyPost($base_url, $username, $password, $uri, $data);

        // TODO: check the status code

        // 200
        list($status_code[4], $cfgver) = $this->getHaProxyCfgVer($base_url, $username, $password);
        $cfgver = trim($cfgver);

        // Add a new ACL to "http" frontend
        /*
        # https://www.haproxy.com/documentation/dataplaneapi/community/?v=v3#post-/services/haproxy/configuration/frontends/-parent_name-/acls/-index-
        curl -X POST --user admin:Hapr0xy001 \
          -H 'Content-Type: application/json' -H 'Accept: application/json' \
          -d '{
          "acl_name": "acl'$CFGVER'",
          "criterion": "req.hdr(Host)",
          "value": "23233.devprov.mermsemr.com"
          }' \
          "http://172.16.4.99:5555/v3/services/haproxy/configuration/frontends/http/acls/0?version=$CFGVER"
        */
        $acl = "acl" . $cfgver0;
        $data = [
            "acl_name" => $acl,
            "criterion" => "req.hdr(Host)",
            "value" => $provisionURL
        ];
        $uri = '/v3/services/haproxy/configuration/frontends/http/acls/0?version=' . $cfgver;
        // 202
        list($status_code[5], $result) = $this->getHaProxyPost($base_url, $username, $password, $uri, $data);

        // TODO: check the status code

        // 200
        list($status_code[6], $cfgver) = $this->getHaProxyCfgVer($base_url, $username, $password);
        $cfgver = trim($cfgver);

        // Add a new Backend Switching Rule to "http" frontend for "use_backend"
        /*
        # https://www.haproxy.com/documentation/dataplaneapi/community/?v=v3#post-/services/haproxy/configuration/frontends/-parent_name-/backend_switching_rules/-index-
        curl -X POST --user admin:Hapr0xy001 \
          -H 'Content-Type: application/json' -H 'Accept: application/json' \
          -d '{
          "name": "'$BACKEND'",
          "cond": "if",
          "cond_test": "'$ACL'"
          }' \
          "http://172.16.4.99:5555/v3/services/haproxy/configuration/frontends/http/backend_switching_rules/0?version=$CFGVER" > use_backend.txt
        */
        $data = [
            "name" => $backend,
            "cond" => "if",
            "cond_test" => $acl
        ];
        $uri = '/v3/services/haproxy/configuration/frontends/http/backend_switching_rules/0?version=' . $cfgver;
        // 202
        list($status_code[7], $result) = $this->getHaProxyPost($base_url, $username, $password, $uri, $data);

        // TODO: check the status code

        // 200
        list($status_code[8], $cfgver) = $this->getHaProxyCfgVer($base_url, $username, $password);
        $cfgver = trim($cfgver);

        $codes = "";
        foreach ($status_code as $i => $code) {
            $codes .= "\n$i => $code";
        }
        return [$status_code[7], "<pre>FIRST CFGVER=$cfgver0\n\n--- CODES: ----$codes\n\nLAST CFGVER=$cfgver</pre>"];
    }

    private function getHaproxyPost($base_url, $username, $password, $uri, $data)
    {
        $url = $base_url . $uri;
        $payload = json_encode($data);
        $headers = array(
            'Content-Type:application/json',
            'Authorization: Basic ' . base64_encode("$username:$password") // <---
        );
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30); //timeout after 30 seconds
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
        curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        // Debug
        //curl_setopt($ch, CURLOPT_VERBOSE, true);
        //$streamVerboseHandle = fopen('php://temp', 'w+');
        //curl_setopt($ch, CURLOPT_STDERR, $streamVerboseHandle);
        // End Debug
        $result = curl_exec($ch);
        $status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);   //get status code
        if ($result === FALSE) {
            $result = sprintf("cUrl error (#%d): %s<br>\n", curl_errno($ch), htmlspecialchars(curl_error($ch)));
        }
        // Debug
        //rewind($streamVerboseHandle);
        //$verboseLog = stream_get_contents($streamVerboseHandle);
        //$result .= "\ncUrl verbose information:\n<pre>" . htmlspecialchars($verboseLog) . "</pre>\n";
        //$result .= "<pre>$payload</pre>\n";
        // End Debug
        curl_close($ch);
        return [$status_code, $result];
    }

    private function getHaproxyGet($base_url, $username, $password, $uri)
    {
        $url = $base_url . $uri;
        $ch = curl_init();
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30); //timeout after 30 seconds
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
        curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
        curl_setopt($ch, CURLOPT_USERPWD, "$username:$password");
        $result = curl_exec($ch);
        $status_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);   //get status code
        curl_close($ch);
        return [$status_code, $result];
    }

    private function getHaproxyCfgVer($base_url, $username, $password)
    {
        return $this->getHaproxyGet($base_url, $username, $password, '/v3/services/haproxy/configuration/version');
    }

}
