<?php


namespace App\Controllers;

class Provision extends BaseController
{


    public function prepareProvision(): string
    {
        $this->allocatePortNo(); //  allocte port for the conyainer use

        $this->prepareDataBase(); //  allocte port for the conyainer use

        $this->updateProvision();

        return 0;
    }

    private function prepareDataBase():string
    {


        return '';
    }
    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/";
        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 = $productId . str_pad($pr->id, 10, "0", STR_PAD_LEFT);
            $partFolder = $ansible_folder . "/parts/" . $provisionUID;

            $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
            $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;
            $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);

            //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 view('welcome_message');
    }

    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");
        }
    }

    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){
        switch ($productId) {
            case "A000001":
            case "A000002":
                $primaryServer = "172.16.4.92";
                break;
            case "A000003":
            case "A000004":
                $primaryServer = "172.16.4.91";
                break;
            case "A000005":
                $primaryServer = "172.16.4.95";
                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){
        try {
            $mysql = "UPDATE members_products SET updated=now() 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;
            $this->APIcall("GET", 'http://10.0.0.32:5006/broadcast/provisionings', $data);

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

    public function releaseProvision(){
        // Use ls command with shell_exec function
//$output = shell_exec('ls -alt /var/www/html/ANSIBLE/');
//
//// Display the list of all files and directories
//echo "<pre>$output</pre>";
//log_message('critical', "Test poath -> ".$output);
        //ANSIBLE/templates/A000001.yml
        $ansibleToken =  $this->ansibleLogin();
        //$mysql = "SELECT id AS plan_id, uid, provision_id, play_file from provision_plans ORDER BY updated ASC LIMIT 1 ";
        $mysql ="SELECT p.product_id, pp.id AS plan_id, pp.uid, pp.provision_id, pp.play_file 
                 FROM provision_plans pp 
                 LEFT JOIN members_products p ON p.id =pp.provision_id 
                 ORDER BY pp.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;
            $playbook = $pr->product_id.".yml";

            $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"=> 1,
                 "debug"=> false,
                 "dry_run"=> false,
                 "playbook"=> $playbook,
                 "environment"=> "{
                     WHAT_HOST_IN_USE:'',
                     DESTINATION_FOLDER:'',
                     WHAT_PART_LOCAL:''
                 }"
              ];
            $this->ansibleProvision($ansibleToken, $params);
        }
        return 0;
    }
    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 );
//            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>";

    }

}
