#!/usr/bin/env php
<?php
$replacePath = $argv[0]; //used to figure out where we're running
global $rootPath;
global $projectRoot;
global $session; //store things here you want to reuse
$session = [];

foreach (get_included_files() as $id => $file) {
    if (strpos($file, "vendor" . DIRECTORY_SEPARATOR . "autoload.php")) {
        $rootPath = str_ireplace("vendor" . DIRECTORY_SEPARATOR . "autoload.php", "", $file);
        break;
    }
}

$projectRoot = str_replace(DIRECTORY_SEPARATOR . "bin", "", __DIR__);

if (empty($rootPath)) {
    $rootPath = str_replace("vendor" . DIRECTORY_SEPARATOR . "tina4stack" . DIRECTORY_SEPARATOR . "tina4php" . DIRECTORY_SEPARATOR . "bin" . DIRECTORY_SEPARATOR . "tina4", "", __FILE__);
    $rootPath = str_replace("bin" . DIRECTORY_SEPARATOR . "tina4", "", $rootPath);
}

require_once "{$rootPath}vendor/autoload.php";

const TINA4_SUPPRESS = true;


if (file_exists($rootPath . "index.php")) {
    include_once $rootPath . "index.php";
}

function writeLine($output): void
{
    echo $output . "\n";
}

function write($output): void
{
    echo $output;
}

function readLn($prefix = "")
{
    if (PHP_OS === 'WINNT') {
        echo $prefix;
        return stream_get_line(STDIN, 1024, PHP_EOL);
    } else {
        return readline($prefix);
    }
}

$menus[] = ["id" => 1, "name" => "initialize", "description" => "Create index.php", "commandLine" => ["options" => ["run"]], "menuCode" => static function ($index, $input, $options) {
    function configureComposerJson($composerFile)
    {
        $composer = json_decode(file_get_contents($composerFile), true);
        $scripts = ["tina4" => "tina4",
            "tina4service" => "tina4service",
            "test" => "@tina4 tests:run",
            "test:verbose" => "@tina4 tests:verbose",
            "initialize" => "@tina4 initialize:run",
            "start" => "@tina4 webservice:run",
            "start-service" => "@tina4service"];
        $classMap = ["src/*", "src/app/*", "src/orm/*", "src/routes/*"];
        $psr4 = ["\\" => ["src/", "src/app/", "src/orm/", "src/routes"]];
        $config = ["process-timeout" => 0];
        if (!isset($composer["scripts"])) {
            $composer["scripts"] = $scripts;
            $composer["classmap"] = $classMap;
            $composer["autoload"] = ["psr-4" => $psr4];
            $composer["config"] = $config;
        }
        file_put_contents($composerFile, str_replace("\/", "/", json_encode($composer, JSON_PRETTY_PRINT)));
    }

    global $rootPath;
    global $projectRoot;
    if ($input === "0") { //Menu Option 0 - Exit
        return ["index" => 0, "prompt" => ""];
    }

    if (!empty($options)) {
        if ($options === "initialize:run") {
            $index = 1;
            $input = "y";

            configureComposerJson($rootPath . "composer.json");
        }
    }
    switch ($index) {
        case 1:
            if ($input === "" || strtolower($input) === "y") {
                if (!file_exists($rootPath . DIRECTORY_SEPARATOR . "index.php")) {
                    if (!file_exists($rootPath . "src")) {
                        $foldersToCopy = ["src/public", "src/app", "src/routes", "src/templates", "src/orm", "src/services", "src/scss"];
                        foreach ($foldersToCopy as $id => $folder) {
                            if (!file_exists($rootPath. DIRECTORY_SEPARATOR . $folder)) {
                                \Tina4\Utilities::recurseCopy($projectRoot .DIRECTORY_SEPARATOR. $folder, $rootPath.DIRECTORY_SEPARATOR . $folder);
                            }
                        }
                    }

                    if (file_exists($rootPath . DIRECTORY_SEPARATOR.".htaccess")) {
                        $htaccessContent = file_get_contents($rootPath.DIRECTORY_SEPARATOR.".htaccess");
                        if (strpos($htaccessContent, "#Block access") === false) {
                            $htaccessContent .= '#Block access to .env and other files
<Files ~ ".env">
Order Allow,Deny
Deny from all
Satisfy all
</Files>

<Files ~ "^.*\.([Hh][Tt][Aa])">
Order allow,deny
Deny from all
Satisfy all
</Files>

#Disable the 3 lines below for just to http
RewriteCond %{HTTPS} !=on
RewriteCond %{REQUEST_URI} !^/.well-known/.*$ [NC]
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301,NE]


RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php [QSA,L]

#Add ability to get the authorization headers on the webserver
SetEnvIf Authorization .+ HTTP_AUTHORIZATION=$0';
                        }
                        file_put_contents($rootPath.DIRECTORY_SEPARATOR.".htaccess", $htaccessContent);
                    } else {
                        copy($projectRoot . DIRECTORY_SEPARATOR.".htaccess", $rootPath . DIRECTORY_SEPARATOR.".htaccess");
                    }

                    $nextIndex = 0;
                    $prompt = "";
                    $indexContent = '<?php' . PHP_EOL . 'require_once "./vendor/autoload.php";' . PHP_EOL . '$config = new \Tina4\Config();'.PHP_EOL.'\Tina4\Initialize();'. PHP_EOL . 'echo new \Tina4\Tina4Php($config);';
                    file_put_contents($rootPath . DIRECTORY_SEPARATOR . "index.php", $indexContent);
                } else {
                    $nextIndex = 0;
                    $prompt = "";
                    write("index.php file already exists, please make a backup of it or remove it to try again");
                    readLn();
                }
            } else {
                $nextIndex = 0;
                $prompt = "";
                write("index.php not created");
                readLn();
            }
            break;
        default:
            $nextIndex = 1;
            $prompt = "Create {$rootPath}index.php?\nIf you already have an index.php, it will not be overwritten! (Y/n):";
            break;
    }
    return ["index" => $nextIndex, "prompt" => $prompt];
}];


$menus[] = ["id" => 2, "name" => "tests", "description" => "Run Tests", "commandLine" => ["options" => ["run", "verbose"]], "menuCode" => static function ($index, $input, $options) use ($argv) {
    global $rootPath;
    $testGroups = [];
    // If test groups have been requested, get the requested group names into a usable array to pass to the run
    if (isset($argv[2])) {
        $testGroups = array_slice($argv, 2);
        $testGroups = implode(" ", $testGroups);
        $testGroups = array_map("trim", explode(",", $testGroups));
    }

    if (!empty($options)) {
        if ($options === "tests:verbose") {
            (new \Tina4\Test($rootPath))->run(false, $testGroups);
        } else {
            (new \Tina4\Test($rootPath))->run(true, $testGroups);
        }
    } else {
        (new \Tina4\Test($rootPath))->run(true, $testGroups);
        readLn();
    }
    return ["index" => 0, "prompt" => ""];
}];


//Helper functions for menu 3
function createDatabaseConnection($databaseType, $databaseName, $databaseHost = "", $username = "", $password = "")
{
    $settings = [];
    switch ($databaseType) {
        case "sqlite3":
            $settings["configString"] = '$DBA = new \Tina4\DataSQLite3("[databasePath]");';
            break;
        case "mysql":
            $settings["configString"] = '$DBA = new \Tina4\DataMySQL("[hostName]/3306:[databasePath]", "[username]", "[password]");';
            break;
        case "firebird":
            $settings["configString"] = '$DBA = new \Tina4\DataFirebird("[hostName]/3050:[databasePath]", "[username]", "[password]");';
            break;
    }

    $settings["configString"] = str_replace('[databasePath]', $databaseName, $settings["configString"]);
    $settings["configString"] = str_replace('[hostName]', $databaseHost, $settings["configString"]);
    $settings["configString"] = str_replace('[username]', $username, $settings["configString"]);
    $settings["configString"] = str_replace('[password]', $password, $settings["configString"]);

    $indexContent = file_get_contents("./index.php");

    //existing record
    $findDBA = '/^\$DBA(.*)/m';
    preg_match($findDBA, $indexContent, $matches, PREG_OFFSET_CAPTURE);

    if (!empty ($matches)) {
        $indexContent = str_replace($matches[0], $settings["configString"], $indexContent);
    } else {
        $indexContent = str_replace('echo new \Tina4\Tina4Php(', 'global $DBA;' . "\n" . $settings["configString"] . "\n" . 'echo new \Tina4\Tina4Php(', $indexContent);
    }

    return $indexContent;
}

$menus[] = ["id" => 3, "name" => "database", "description" => "Create database connection", "menuCode" => static function ($index, $input) {
    global $settings;

    if ($input === "0") { //Menu Option 0 - Exit
        return ["index" => 0, "prompt" => ""];
    }

    if ($index === 1 && $input === "2") {
        $index = 2; //MySQL
    }

    if ($index === 1 && $input === "3") {
        $index = 3; //Firebird
    }

    switch ($index) {
        case 1: //Sqlite
            $nextIndex = 11;
            $settings["databaseType"] = "sqlite3";
            $settings["databaseHost"] = "";
            $settings["databaseUsername"] = "";
            $prompt = "Path and name of database (Example: /home/test.db) :";
            break;
        case 11:
            if ($settings["databaseType"] === "sqlite3") {
                $settings["databasePath"] = $input;
            } else {
                $settings["databasePassword"] = $input;
            }

            $nextIndex = 12;
            $prompt = "Add database configuration to the index.php {$input} (Y/n) ?";
            break;
        case 12:
            $indexContent = createDatabaseConnection($settings["databaseType"], $settings["databasePath"], $settings["databaseHost"], $settings["databaseUsername"], $settings["databasePassword"]);
            if ($input === "y") {
                file_put_contents("./index.php", $indexContent);
            } else {
                writeLine($indexContent);
            }
            $nextIndex = 0;
            break;
        case 2: //MySQL
            $nextIndex = 21;
            $settings["databaseType"] = "mysql";
            $prompt = "Hostname (Example: 127.0.0.1) :";
            break;
        case 3: //Firebird
            $nextIndex = 21;
            $settings["databaseType"] = "firebird";
            $prompt = "Hostname (Example: 127.0.0.1) :";
            break;
        case 21:
            $nextIndex = 22;
            $settings["databaseHost"] = $input;
            switch ($settings["databaseType"]) {
                case "mysql":
                    $example = "test_db";
                    break;
                case "firebird":
                    $example = "/home/firebird/TEST.FDB";
                    break;
            }
            $prompt = "Database Name (Example: {$example}') :";
            break;
        case 22:
            $settings["databasePath"] = $input;
            $nextIndex = 23;
            $prompt = "Username (Example: admin) :";
            break;
        case 23:
            $settings["databaseUsername"] = $input;
            $nextIndex = 11;
            $prompt = "Password (Example: admin) :";
            break;
        default:
            $nextIndex = 1;
            $prompt = "Choose database type:\n";
            $prompt .= "1. Sqlite3 \n";
            $prompt .= "2. MySQL \n";
            $prompt .= "3. Firebird \n";
            $prompt .= "0. Exit \n";
            $prompt .= "Choose:";
            break;
    }
    return ["index" => $nextIndex, "prompt" => $prompt];
}];

$menus[] = ["id" => 4, "name" => "orm", "description" => "Create orm objects", "menuCode" => static function ($index, $input) {

    global $DBA;
    global $session;
    global $rootPath;

    if ($input === "0") {
        return ["index" => 0, "prompt" => ""];
    }

    if (empty($DBA)) {
        $nextIndex = 1;
        $prompt = "Please setup a global \$DBA variable and assign a database connection";
    } else {

        if (empty($session["database"])) {
            $database = $DBA->getDatabase();
            $session["database"] = $database;
        } else {
            $database = $session["database"];
        }

        switch ($index) {
            case 1:
                $nextIndex = 2;
                $no = 0;
                foreach ($database as $table => $fields) {
                    $no++;
                    if ($no == $input || $input == "all") {
                        echo str_repeat("=", 40) . "\n";
                        echo "Table: {$table}\n";
                        echo str_repeat("=", 40) . "\n";
                        foreach ($fields as $id => $field) {
                            echo "{$field->fieldName}\n";
                        }
                        $session["tableData"][] = ["table" => $table, "fields" => $fields];
                    }
                }

                if ($input == "all") {
                    writeLine("You want to convert all the tables in the database ? ");
                }

                $prompt = "Is this correct ? (y/n):";
                break;
            case 2:
                $nextIndex = -1;

                if ($input === "y") {
                    $record = new \Tina4\DataRecord();
                    foreach ($session["tableData"] as $id => $data) {
                        $className = ucwords($record->getObjectName($data["table"]));

                        $fields = [];
                        $fieldMapping = [];
                        $primaryKey = "";
                        foreach ($data["fields"] as $fid => $field) {
                            if ($fid == 0 || $field->isPrimaryKey === 1) {
                                $primaryKey = $field->fieldName;
                            }
                            $fieldMapping [] = '"' . $record->getObjectName($field->fieldName) . '" => "' . $field->fieldName . '"';
                            $fields[] = "\tpublic \$" . $record->getObjectName($field->fieldName) . ";";
                        }

                        $definition = '<?php
class [CLASS_NAME] extends \Tina4\ORM
{
    public $tableName="[TABLE_NAME]";
    public $primaryKey="[PRIMARY_KEY]"; //set for primary key
    public $fieldMapping = [[FIELD_MAPPING]];
    //public $softDelete=true; //uncomment for soft deletes in crud
    
[FIELDS]
}';

                        $fileName = $rootPath . "src" . DIRECTORY_SEPARATOR . "orm" . DIRECTORY_SEPARATOR . $className . ".php";

                        echo "Creating object in {$fileName} {$className}\n";
                        if (!file_exists($fileName)) {
                            $definition = str_replace("[PRIMARY_KEY]", $record->getObjectName($primaryKey), $definition);
                            $definition = str_replace("[TABLE_NAME]", $data["table"], $definition);
                            $definition = str_replace("[CLASS_NAME]", $className, $definition);
                            $definition = str_replace("[FIELDS]", join("\n", $fields), $definition);
                            $definition = str_replace("[FIELD_MAPPING]", join(",", $fieldMapping), $definition);
                            file_put_contents($fileName, $definition);
                        }
                    }
                    $prompt = "Done, press Enter to continue!";
                } else {
                    $prompt = "Press Enter to continue, no object created!";
                }
                $session["tableData"] = null;
                break;
            default:
                $nextIndex = 1;
                $no = 0;
                foreach ($database as $table => $fields) {
                    $no++;
                    writeLine("{$no}.) {$table}");
                }
                writeLine("0.) Exit");
                writeLine("Type 'all' to convert all the tables, no existing objects will be over written");

                $prompt = "Choose table:";
                break;
        }
    }
    return ["index" => $nextIndex, "prompt" => $prompt];
}];

addMenu($menus, "webservice", "Runs webservice", static function ($index, $input) use ($argv) {
    $port = $argv[2] ?? "7145";
    \Tina4\Debug::message("Server running on http://localhost:{$port}");
    if (strpos(php_uname(), "Linux") !== false) {
        `php -S 0.0.0.0:{$port} index.php`;
    } else {
        `php -S localhost:{$port} index.php`;
    }

}, true,  ["options" => ["run"]]);


/**
 * Menus are created and have a function "menuCode" which takes on the following inputs ($index, $input, $options)
 * The index is handled in a switch statement to parse the input, the menuCode function always should return
 * ["index" => , "prompt" => ], if you want it to end you need to return ["index" => 0, "prompt" => ""];
 *
 * $menus[] = ["id" => 2, "name" => "Do something", "menuCode" => static function($index, $input, $options) {
 * return ["index" => 0, "prompt" => ""];
 * }];
 */

function addMenu(&$menus, $name,  $description , $menuCode, $canShow=true, $commandLineOptions=[]) : void
{
    $menus[] = ["id" => count($menus)+1, "name" => $name, "menuCode" => $menuCode, "show" => $canShow, "description" => $description, "commandLine" => $commandLineOptions];
}


/**
 * Draws a menu based on the array above for display to the user
 * @param $menus
 */
function drawMenu($menus)
{
    global $rootPath;
    $menuId = 0;
    $input = "";
    $index = 0;

    while ($input !== "quit") {
        write("\e[H\e[J"); //clear screen
        writeLine(str_repeat("=", 100));
        writeLine("TINA4 - MENU ({$rootPath})");
        writeLine(str_repeat("=", 100));

        if ($menuId !== 0) {
            $found = false;
            foreach ($menus as $mid => $menu) {
                if ($menuId === $menu["id"]) {
                    $prompt = call_user_func($menu["menuCode"], $index, $input, null);
                    $index = $prompt["index"];
                    if ($index === 0) {
                        $menuId = 0;
                    } else {
                        $input = readLn($prompt["prompt"]);
                        $found = true;
                    }
                }
            }
            if (!$found) {
                $menuId = 0;
            }
        } else {
            foreach ($menus as $id => $menu) {
                if (!isset($menu["show"])) {
                    $menu["show"] = true;
                }
                if ($menu["show"]) {
                    writeLine(($menu["id"]) . ".) " . $menu["description"]);
                }

            }
            $input = readLn("Choose menu option or type \"quit\" to Exit:");
            $menuId = (int)$input;
        }
    }
}

if (count($argv) === 1) {
    drawMenu($menus);
    echo "\e[H\e[J"; //clear screen
} else {
    //write("\e[H\e[J");
    $options = [];
    $options[] = ["command" => "help", "description" => "Gets the help menu"];
    foreach ($menus as $mid => $menu) {
        if (isset($menu["commandLine"])) {
            foreach ($menu["commandLine"]["options"] as $oid => $option) {
                $options[] = ["command" => $menu["name"] . ":" . $option, "description" => $menu["description"], "function" => $menu["menuCode"]];
            }
        }
    }

    if (isset($argv[1])) {
        writeLine("Running: " . $argv[0] . " " . $argv[1]);
    }

    if (strtolower($argv[1]) === "help") {
        writeLine("Available options:");
        foreach ($options as $oid => $option) {
            writeLine(str_pad($option["command"] . " ", 30, ".", STR_PAD_RIGHT) . $option["description"]);
        }
    } else {

        $found = false;
        foreach ($options as $oid => $option) {
            if ($option["command"] === $argv[1]) {
                call_user_func($option["function"], 0, "", $option["command"]);
            }
        }
    }
}

//End of the console app, say something nice!
$comments[] = "Have a great day!";
$comments[] = "Keep up the good work!";
$comments[] = "You are a star!";
$comments[] = "Nothing is impossible!";
$comments[] = "Keep learning!";
$comments[] = "Live long and prosper!";
$comments[] = "Life is lemons, make lemonade!";

$complimentary = $comments[rand(0, count($comments) - 1)];
writeLine("");
echo "Thank you for using Tina4 - {$complimentary}\n";