<?php

class Host_generate_model extends Cf_Model
{
    public $nokeptCount;
    public $hostCount = 1;
    public $withSoftware = 0;
    public $interval = 60;
    public $verbose = false;
    public $hostKey;
    protected $cfdb;

    protected $n = 2;

    public function setDb($db)
    {
        $this->cfdb = $db;
        $this->cfdb->save_queries = false;
    }

    public function generateHost($hostKey)
    {
        $sql =
            'INSERT INTO "__hosts" ("hostkey", "ipaddress", "iscallcollected", "lastreporttimestamp", "firstreporttimestamp") VALUES (\'%s\', \'1.2.3.4\', \'false\', \'now()\', \'now()\');';
        $sql = sprintf($sql, $hostKey);
        $this->cfdb->query($sql);
    }

    public function generateHosts($hostKey, $count)
    {
        $sql =
            'INSERT INTO "__hosts" ("hostkey", "ipaddress", "iscallcollected", "lastreporttimestamp", "firstreporttimestamp") VALUES ';
        for ($i = 1; $i <= $count; $i++) {
            $nextHostkey = $hostKey . $i;

            $nextIpAddr = '';
            for ($j = 0; $j < 4; $j++) {
                if ($j > 0) {
                    $nextIpAddr = '.' . $nextIpAddr;
                }
                $nextIpAddr = intdiv($i, 256 ** $j) % 256 . $nextIpAddr;
            }

            $sql .= "('$nextHostkey', '$nextIpAddr', 'false', 'now()', 'now()')";
            if ($i != $count) {
                $sql .= ',';
            }
        }
        $this->cfdb->query($sql);
    }

    public function generateLastSeenHosts($hostKey, $count)
    {
        $sql =
            'INSERT INTO "%s" ("hostkey", "compo", "lastseendirection", "remotehostkey", "remotehostip", "lastseentimestamp", "lastseeninterval") VALUES ';
        for ($i = 1; $i <= $count; $i++) {
            $nextHostkey = $hostKey . $i;
            $ip = mt_rand(1, 255) . '.' . mt_rand(1, 255) . '.' . mt_rand(1, 255) . '.' . mt_rand(1, 255);
            $sql .= "('$nextHostkey', 'INCOMING.$nextHostkey', 'INCOMING', '$nextHostkey', '$ip', 'now()', '300')";
            if ($i != $count) {
                $sql .= ',';
            }
        }
        $this->cfdb->query(sprintf($sql, '__lastseenhosts'));
    }

    public function generateVariables($hostKey)
    {
        $data = include __DIR__ . '/../data/variables.php';
        $sql =
            'INSERT INTO "__variables" ("hostkey", "namespace", "bundle", "variablename", "variablevalue", "variabletype", "comp", "metatags", "changetimestamp") VALUES ';
        foreach ($data as $key => $d) {
            $sql .= vsprintf("('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')", $d);
            if (isset($data[$key + 1])) {
                $sql .= ',';
            }
        }
        $this->cfdb->query($sql);
    }

    public function generateSoftware($hostKey)
    {
        $sql = file_get_contents(__DIR__ . '/../data/software.sql');
        $sql = str_replace('%hostkey%', $hostKey, $sql);
        $sql = str_replace('%timestamp%', date('Y-m-d H:i:s+00'), $sql);
        $this->cfdb->query($sql);

        $sql = file_get_contents(__DIR__ . '/../data/softwareupdates.sql');
        $sql = str_replace('%hostkey%', $hostKey, $sql);
        $sql = str_replace('%timestamp%', date('Y-m-d H:i:s+00'), $sql);
        $this->cfdb->query($sql);
    }

    public function generateCMDB($hostKey, $epoch)
    {
        $sql = file_get_contents(__DIR__ . '/../data/cmdb.sql');
        $sql = str_replace('%hostkey%', $hostKey . $epoch, $sql);
        $sql = str_replace('%timestamp%', date('Y-m-d H:i:s+00'), $sql);
        $sql = str_replace('%epoch%', $epoch, $sql);
        $this->cfdb->query($sql);
    }

    public function generatePromiseExecutions($hostKey)
    {
        $sql = file_get_contents(__DIR__ . '/../data/promiseexecutions.sql');
        $sql = str_replace('%hostkey%', $hostKey, $sql);
        $sql = str_replace('%timestamp%', date('Y-m-d H:i:s+00'), $sql);
        $this->cfdb->query($sql);
    }

    public function generatePromiseLog($hostKey)
    {
        $sql = file_get_contents(__DIR__ . '/../data/promiselog.sql');
        $sql = str_replace('%hostkey%', $hostKey, $sql);
        $sql = str_replace('%timestamp%', date('Y-m-d H:i:s+00'), $sql);
        $this->cfdb->query($sql);
    }

    public function generateContexts($hostKey)
    {
        $data = include __DIR__ . '/../data/contexts.php';
        $sql = 'INSERT INTO "__contexts" ("hostkey", "contextname", "metatags", "changetimestamp")  VALUES ';
        foreach ($data as $key => $d) {
            $sql .= vsprintf("('%s', '%s', '%s', '%s')", $d);
            if (isset($data[$key + 1])) {
                $sql .= ',';
            }
        }
        $this->cfdb->query($sql);
    }

    public function bulkGenerateContexts($hostKeyPattern, $count)
    {
        $sql = 'INSERT INTO "__contexts" ("hostkey", "contextname", "metatags", "changetimestamp")  VALUES ';
        for ($i = 1; $i <= $count; $i++) {
            $hostKey = $hostKeyPattern . $i;
            $data = include __DIR__ . '/../data/contexts.php';
            foreach ($data as $key => $d) {
                $sql .= vsprintf("('%s', '%s', '%s', '%s')", $d);
                if ($i != $count || isset($data[$key + 1])) {
                    $sql .= ',';
                }
            }
        }
        $this->cfdb->query($sql);
    }

    public function bulkGenerateVariables($hostKeyPattern, $count)
    {
        $initialSql = $sql =
            'INSERT INTO "__variables" ("hostkey", "namespace", "bundle", "variablename", "variablevalue", "variabletype", "comp", "metatags", "changetimestamp") VALUES ';
        for ($i = 1; $i <= $count; $i++) {
            $hostKey = $hostKeyPattern . $i;
            $data = include __DIR__ . '/../data/variables.php';
            foreach ($data as $key => $d) {
                $sql .= vsprintf("('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s')", $d);
                $sql .= ',';
            }

            if ($i % 500 == 0) {
                $sql = trim($sql, ',');
                $this->cfdb->query($sql);
                $sql = $initialSql;
            }
        }

        if ($count % 500 != 0) {
            $sql = trim($sql, ',');
            $this->cfdb->query($sql);
        }
    }

    public function updateContextCache()
    {
        $this->cfdb->query('TRUNCATE TABLE ContextCache;');
        $this->cfdb->query('INSERT INTO ContextCache (hostkey, contextvector)
    SELECT hostkey, to_tsvector(\'simple\', replace(x::text,\'_\',\'.\'))
    FROM (
        SELECT hostkey, array_agg(contextname) as x
        FROM __Contexts
        GROUP BY hostkey
        ) as sub;');
    }

    public function updateMaterializedViews()
    {
        $this->cfdb->query('CALL update_inventory();');
    }

    public function generateAgentStatus($hostKey, $status)
    {
        $this->cfdb->query(
            "INSERT INTO __agentstatus (hostkey, agentexecutioninterval, lastagentlocalexecutiontimestamp, lastagentexecutionstatus) VALUES
                          ('$hostKey', 300,	now(), '" .
                $status .
                "');",
        );
    }

    public function bulkGenerateAgentStatus($hostKeyPattern, $count, $failCount)
    {
        $sql =
            'INSERT INTO __agentstatus (hostkey, agentexecutioninterval, lastagentlocalexecutiontimestamp, lastagentexecutionstatus) VALUES ';
        for ($i = 1; $i <= $count; $i++) {
            $hostKey = $hostKeyPattern . $i;
            $status = $i > $failCount ? 'OK' : 'FAIL';
            $sql .= "('$hostKey', 300,	now(), '" . $status . "'),";
        }
        $sql = trim($sql, ',');
        $this->cfdb->query($sql);
    }

    public function setNotKept($hostKey, $i)
    {
        if ($i == $this->n) {
            $this->n += 5;
        } else {
            return false;
        }

        $sql = <<<SQL
                INSERT INTO "__promiseexecutions" ("hostkey", "policyfile", "releaseid", "promisehash", "namespace", "bundlename", "promisetype", "promiser", "stackpath", "promisehandle", "promiseoutcome", "logmessages", "promisees", "changetimestamp")
                VALUES ('%s', 'file.sh', NULL, 'promisehash', 'default', 'cfe_internal_enterprise_main', 'methods', NULL, 'cfe_internal_enterprise_main', NULL, 'NOTKEPT', NULL, NULL, NULL);
        SQL;
        $this->cfdb->query(sprintf($sql, $hostKey));
    }

    public function generateContainers($hostKey, $count = 30)
    {
        $states = ['running', 'paused', 'exited'];
        $engines = ['docker', 'podman'];
        $owners = ['root', 'cfengine', 'app', 'ubuntu'];
        $repositories = [
            'nginx',
            'redis',
            'postgres',
            'alpine',
            'busybox',
            'mysql',
            'rabbitmq',
            'mongo',
            'httpd',
            'node',
        ];
        $commands = [
            '/docker-entrypoint.sh nginx -g \"daemon off;\"',
            'redis-server',
            'postgres',
            '/bin/sh',
            'sleep infinity',
            'mysqld',
            'rabbitmq-server',
            'mongod',
            'httpd-foreground',
            'node app.js',
        ];

        $sql =
            'INSERT INTO "__containers" ("hostkey", "owner", "id", "name", "image", "image_id", "command", "state", "ports", "engine", "created_at", "started_at", "finished_at", "collected_at", "details") VALUES ';
        $values = [];
        for ($i = 1; $i <= $count; $i++) {
            $owner = $owners[$i % count($owners)];
            $id = bin2hex(random_bytes(32));
            $imageId = 'sha256:' . bin2hex(random_bytes(32));
            $repo = $repositories[$i % count($repositories)];
            $tag = $i % 3 === 0 ? 'latest' : ($i % 3 === 1 ? '1.0' : 'stable');
            $image = $repo . ':' . $tag;
            $name = '/' . $repo . '-' . $i;
            $command = $commands[$i % count($commands)];
            $state = $states[$i % count($states)];
            $engine = $engines[$i % count($engines)];
            // created_at sits before started_at; finished_at only exists for
            // containers in the `exited` state, otherwise NULL.
            $startedTs = time() - mt_rand(60, 60 * 60 * 24 * 30);
            $createdTs = $startedTs - mt_rand(60, 60 * 60 * 24);
            $startedAt = date('Y-m-d H:i:s+00', $startedTs);
            $createdAt = date('Y-m-d H:i:s+00', $createdTs);
            $finishedAt = $state === 'exited' ? date('Y-m-d H:i:s+00', $startedTs + mt_rand(60, 60 * 60)) : null;
            $port1 = 8000 + $i;
            $port2 = 9000 + $i;
            $ports = json_encode([
                [
                    'container_port' => $port1,
                    'host_ip' => '',
                    'host_port' => $port1,
                    'protocol' => 'tcp',
                ],
                [
                    'container_port' => $port2,
                    'host_ip' => '0.0.0.0',
                    'host_port' => $port2,
                    'protocol' => $i % 4 === 0 ? 'udp' : 'tcp',
                ],
            ]);
            $details = json_encode([
                'RestartCount' => mt_rand(0, 5),
                'Platform' => 'linux/amd64',
                'Labels' => [
                    'maintainer' => 'cfengine',
                    'env' => $i % 2 === 0 ? 'prod' : 'dev',
                ],
            ]);

            $values[] = sprintf(
                "('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', %s, '%s', '%s')",
                $this->cfdb->escape_str($hostKey),
                $this->cfdb->escape_str($owner),
                $this->cfdb->escape_str($id),
                $this->cfdb->escape_str($name),
                $this->cfdb->escape_str($image),
                $this->cfdb->escape_str($imageId),
                $this->cfdb->escape_str($command),
                $state,
                $this->cfdb->escape_str($ports),
                $engine,
                $createdAt,
                $startedAt,
                $finishedAt === null ? 'NULL' : "'$finishedAt'",
                date('Y-m-d H:i:s+00'),
                $this->cfdb->escape_str($details),
            );
        }
        $this->cfdb->query($sql . implode(',', $values));
    }

    public function generateContainerImages($hostKey, $count = 30)
    {
        $repositories = [
            'nginx',
            'redis',
            'postgres',
            'alpine',
            'busybox',
            'mysql',
            'rabbitmq',
            'mongo',
            'httpd',
            'node',
            'python',
            'golang',
            'php',
            'ruby',
            'ubuntu',
        ];
        $tags = ['latest', '1.0', '2.0', 'stable', 'alpine', 'slim', 'bullseye'];
        $owners = ['root', 'cfengine', 'app', 'ubuntu'];
        $engines = ['docker', 'podman'];

        $sql =
            'INSERT INTO "__container_images" ("hostkey", "owner", "id", "repository", "tag", "size_bytes", "engine", "created_at", "collected_at") VALUES ';
        $values = [];
        for ($i = 1; $i <= $count; $i++) {
            $owner = $owners[$i % count($owners)];
            $id = 'sha256:' . bin2hex(random_bytes(32));
            $repo = $repositories[$i % count($repositories)];
            $tag = $tags[$i % count($tags)];
            $sizeBytes = mt_rand(5 * 1024 * 1024, 800 * 1024 * 1024);
            $engine = $engines[$i % count($engines)];
            $createdAt = date('Y-m-d H:i:s+00', time() - mt_rand(60 * 60 * 24, 60 * 60 * 24 * 365));

            $values[] = sprintf(
                "('%s', '%s', '%s', '%s', '%s', %d, '%s', '%s', '%s')",
                $this->cfdb->escape_str($hostKey),
                $this->cfdb->escape_str($owner),
                $this->cfdb->escape_str($id),
                $this->cfdb->escape_str($repo),
                $this->cfdb->escape_str($tag),
                $sizeBytes,
                $engine,
                $createdAt,
                date('Y-m-d H:i:s+00'),
            );
        }
        $this->cfdb->query($sql . implode(',', $values));
    }

    public function generateContainerVolumes($hostKey, $count = 30)
    {
        $drivers = ['local', 'nfs', 'overlay', 'tmpfs'];
        $owners = ['root', 'cfengine', 'app', 'ubuntu'];
        $engines = ['docker', 'podman'];

        $sql =
            'INSERT INTO "__container_volumes" ("hostkey", "owner", "name", "driver", "size_bytes", "engine", "created_at", "collected_at") VALUES ';
        $values = [];
        for ($i = 1; $i <= $count; $i++) {
            $owner = $owners[$i % count($owners)];
            $name = 'vol_' . bin2hex(random_bytes(8)) . '_' . $i;
            $driver = $drivers[$i % count($drivers)];
            $sizeBytes = mt_rand(1024 * 1024, 10 * 1024 * 1024 * 1024);
            $engine = $engines[$i % count($engines)];
            $createdAt = date('Y-m-d H:i:s+00', time() - mt_rand(60 * 60, 60 * 60 * 24 * 180));

            $values[] = sprintf(
                "('%s', '%s', '%s', '%s', %d, '%s', '%s', '%s')",
                $this->cfdb->escape_str($hostKey),
                $this->cfdb->escape_str($owner),
                $this->cfdb->escape_str($name),
                $driver,
                $sizeBytes,
                $engine,
                $createdAt,
                date('Y-m-d H:i:s+00'),
            );
        }
        $this->cfdb->query($sql . implode(',', $values));
    }

    public function generateMeasurementsData($hostKey)
    {
        $this
            ->cfdb->query("INSERT INTO __monitoringmgmeta (hostkey, observable, global, expected_min, expected_max, unit, description, updatedtimestamp) VALUES
                          ('$hostKey',	'cpu',	'1',	0,	100,	'percent',	'%CPU utilization (all)',	now());");

        $id = $this->cfdb->insert_id();

        $this
            ->cfdb->query("INSERT INTO \"__monitoringmg\" (\"meta_id\", \"sample\", \"value1\", \"value2\", \"value3\", \"value4\", \"updatedtimestamp\") VALUES
                           ($id,	1, 10.1,	7.35,	7.7,	0,	now() + '5 minutes'::interval),
                           ($id,	2, 1.34,	3.01,	5.58,	0,	now() + '10 minutes'::interval),
                           ($id,	3, 1.39,	3.06,	5.58,	0,	now() + '15 minutes'::interval),
                           ($id,	4, 9.8 ,	7.14,	7.45,	0,	now() + '20 minutes'::interval),
                           ($id,	5, 9.85,	7.16,	7.53,	0,	now() + '25 minutes'::interval),
                           ($id,	6, 1.41,	3.05,	5.45,	0,	now() + '30 minutes'::interval);");
    }
}
