<?php

namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
use Doctrine\DBAL\Exception\TableNotFoundException;

use Symfony\Bundle\FrameworkBundle\Console\Application;
use Symfony\Component\Console\Input\ArrayInput;
use Symfony\Component\Console\Output\BufferedOutput;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\Extension\Core\Type as FormType;
use Symfony\Component\Yaml\Yaml;
use Doctrine\ORM\EntityManagerInterface;

use App\Utility\NullTranslator;

use App\Entity\WebrootUser;
use App\Entity\WebrootRole;
use App\Entity\WebrootFilePermission;
use App\Entity\WebrootFile;

abstract class WebrootSetupController extends AbstractController
{
    /**
     * 
     * @var string|null Project root directory.
     */
    private ?KernelInterface $kernel = null;
    private ?string $project_dir = null;
    private ?UserPasswordHasherInterface $password_hasher=null;
    private ?TranslatorInterface $translator=null;
    private ?EntityManagerInterface $entitymanager=null;
    private ?NullTranslator $nulltranslator=null;
    private const INITIAL_SETUP_COOKIE = "--webroot-initial-setup--";
    
    
    public function __construct(KernelInterface $kernel, 
            UserPasswordHasherInterface $password_hasher,
            TranslatorInterface $translator,
            EntityManagerInterface $entytymanager) {
        $this->kernel = $kernel;
        $this->project_dir = $kernel->getProjectDir();
        $this->password_hasher = $password_hasher;
        $this->translator = $translator;
        $this->nulltranslator = new NullTranslator();
        $this->entitymanager = $entytymanager;
    }
    
    /**
     * @brief Get the root directory of the Project.
     * 
     * This method gets the root directory of the project as an absolute path
     * as returned by the Kernel->getProjectDir();
     * 
     * @return string|null The root directory of the prject or null if the 
     * Kernel cannot determine it.
     */
    public function getProjectDir(): ?string
    {
        return $this->project_dir;
    }
    
    /**
     * @brief Check if there is an Administrator account existing. 
     * @return bool `true` if an Administrator is existent in the database.
     */
    protected function hasAdministrator(): bool
    {
        try {
            $repos = $this->entitymanager->getRepository(WebrootUser::class);
        } catch (\Throwable $ex) {
            return false;
        }
        return $repos->hasAdministrator();        
    }
    
    /**
     * @brief Check if user is logged in for setup.
     * @return bool Returns `true` if user is logged in and is allowed to view
     *      the setup pages.
     */
    public function userIsLoggedInAsAdmin(): bool
    {
        $user = $this->getUser();
        if ($user && $user->isAdmin()) {
            return true;
        }
    
        return false;
    }

    public function getKernel(): Kernel
    {
        return $this->kernel;
    }
    
    public function getTranslator(): TranslatorInterface
    {
        return $this->translator;
    }
    
    public function trans(string $message,array $args=[],?string $locale=null,?string $domain=null) : string
    {
        return $this->translator->trans($message,$args,locale:$locale,domain:$domain);
    }
    
    public function translate(string $message,array $args=[],?string $locale=null,?string $domain=null) :string
    {
        return $this->translator->trans($message,$args,locale:$locale,domain:$domain);
    }
    
    /**
     * @brief Check if we are already set up.
     * 
     * This method checks if there is an .env.local file installed in the 
     * system and returns ture if the .env.local file is found.
     * 
     * @return bool `true` if we are already installed.
     */
    public function isInstalled(): bool
    {
        return (file_exists(join(DIRECTORY_SEPARATOR,[$this->project_dir,".env"]))
                && file_exists(join(DIRECTORY_SEPARATOR,[$this->project_dir,".env.local"])));
    }
    
    /**
     * 
     * @return string
     */
    protected function runMakeMigration(): string
    {
        $application = new Application($this->kernel);
        $application->setAutoExit(false);
        
        $input = new ArrayInput([
            "command" => "make:migration",
            "--formatted" => true,
            "--no-interaction" => true,
            "--quiet" => true,
        ]);
        
        $output = new BufferedOutput();
        $errc =$application->run($input,$output);
        
        
        $content=$output->fetch();
        
        if ($errc) {
            throw new \Exception("make:migration failed!\n" . $content);
        }
        
        return $content;
    }
    
    protected function runCreateDatabase(): string
    {
        $application = new Application($this->kernel);
        $application->setAutoExit(false);
        
        $input = new ArrayInput([
            "command" => "doctrine:database:create",
            "--no-interaction" => true,
            "--quiet" => true,
        ]);
        
        $output = new BufferedOutput();
        $errc = $application->run($input,$output);
        
        $content = $output->fetch();
        if ($errc) {
            throw new \Exception("doctrine:database:create failed!\n". $content);
        }
        
        return $content;
    }
    
    /**
     * 
     * @return string
     */
    protected function runMigrate(): string
    {
        $application = new Application($this->kernel);
        $application->setAutoExit(false);
        
        $input = new ArrayInput([
            "command" => "doctrine:migration:migrate",
            "--no-interaction" => true,
            "--quiet" => true,
        ]);
        
        $output = new BufferedOutput();
        $errcode = $application->run($input,$output);
        if ($errcode) {
            throw new \Exception("runMigrate failed!\n". $output->fetch());
        }
        
        $content = $output->fetch();
        
        return $content;
    }
    
    /**
     * 
     * @return string
     */
    protected function runCacheClear(): string
    {
        $application = new Application($this->kernel);
        $application->setAutoExit(false);
        
        $input = new ArrayInput([
            "command" => "cache:clear",
            "--no-interaction" => true,
            "--quiet" => true,
        ]);
        
        $output = new BufferedOutput();
        $errcode = $application->run($input,$output);
        if ($errcode) {
            throw new \Exception("runMigrate failed!\n". $output->fetch());
        }
        
        $content = $output->fetch();
        
        return $content;
    }
    
    protected function runDatabaseUpdate(): string
    {
        $application = new Application($this->kernel);
        $application->setAutoExit(false);
        
        $input = new ArrayInput([
            "command" => "doctrine:schema:update",
            "--no-interaction" => true,
            "--quiet" => true,
        ]);
        
        $output = new BufferedOutput();
        $errcode = $application->run($input,$output);
        if ($errcode) {
            throw new \Exception("runMigrate failed!\n". $output->fetch());
        }
        
        $content = $output->fetch();
        
        return $content;
    }
    
    
    public function getInitialRoles(bool $translated=true): array
    {
        $domain="mydevel.webroot";
        if (0) {
            //should never be executed just here to be able to tranlsate
            $this->nulltrans->trans("webroot-user.name",domain:"mydevel.webroot.role");
            $this->nulltrans->trans("webroot-user.decr",domain:"mydevel.webroot.role");
        }
        
        $roles = [
                [
                    "role"=>"ROLE_SUPERADMIN",
                    "name"=>$this->nulltranslator->trans("superadmin.name",domain:"mydevel.webroot.role"),
                    "description"=>$this->nulltranslator->trans("superadmin.descr",domain:"mydevel.webroot.role"),
                ],[
                    "role"=>"ROLE_ADMIN",
                    "name"=>$this->nulltranslator->trans("admin.name",domain:"mydevel.webroot.role"),
                    "description"=>$this->nulltranslator->trans("admin.descr",domain:"mydevel.webroot.role"),
                ],[
                    "role"=>"ROLE_USER",
                    "name"=>$this->nulltranslator->trans("user.name",domain:"mydevel.webroot.role"),
                    "description"=>$this->nulltranslator->trans("user.descr",domain:"mydevel.webroot.role"),
                ],[
                    "role"=>"ROLE_PUBLIC",
                    "name"=>$this->nulltranslator->trans("public.name",domain:"mydevel.webroot.role"),
                    "description"=>$this->nulltranslator->trans("public.descr",domain:"mydevel.webroot.role"),
                ],
            ];
        if (!$translated) {
            return $roles;
        }
        $ret = [];
        foreach ($roles as $role) {
            $ret[] = [
                "role" => $role["role"],
                "name" => $this->trans($role["name"],domain:$domain),
                "description" => $this->trans($role["description"],domain:$domain),
            ];
        }
        return $ret;
    }
    
    public function createSetupForm(): Form
    {
        return $this->createFormBuilder()
                ->add('env', FormType\ChoiceType::class,[
                    "label"=>$this->translator->trans("setupform.env.label",domain:"mydevel.webroot.setup"),
                    "mapped"=>false,
                    "required"=>true,
                    "help"=>$this->trans("setupform.env.help",domain:"mydevel.webroot.setup"),
                    "choices"=>[
                        $this->translator->trans("setupform.env.choices.prod",domain:"mydevel.webroot.setup") => "prod",
                        $this->translator->trans("setupform.env.choices.dev",domain:"mydevel.webroot.setup") => "dev",
                    ],
                    "attr"=>["class"=>"full-width"],
                    "data" => "prod"])
                ->add('locale',FormType\TextType::class,[
                    "mapped"=>false,
                    "label"=>$this->trans("setupform.locale.label",domain:"mydevel.webroot.setup"),
                    "help"=>$this->trans("setupform.locale.help",domain:"mydevel.webroot.setup"),
                    "required"=>false,
                    "attr"=>["class"=>"full-width"]])
                ->add('tempdir',FormType\TextType::class,[
                    "mapped"=>false,
                    "label"=>$this->trans("setupform.tempdir.label",domain:"mydevel.webroot.setup"),
                    "required"=>false,
                    "attr"=>["class"=>"full-width"]])
                ->add('site_name',FormType\TextType::class,[
                    "mapped"=>false,
                    "required"=>true,
                    "help"=>$this->trans("setupform.site.name.help",domain:"mydevel.webroot.setup"),
                    "label" => $this->trans("setupform.site.name.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["class"=>"full-width"]])
                ->add('site_rootdir',FormType\TextType::class,[
                    "mapped"=>false,
                    "required"=>true,
                    "help"=>$this->trans("setupform.site.root.help",domain:"mydevel.webroot.setup"),
                    "label" => $this->trans("setupform.site.root.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["class"=>"full-width"]])
                ->add('site_email',FormType\EmailType::class,[
                    "mapped"=>false,
                    "required"=>false,
                    "label" => $this->trans("setupform.site.email.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["class"=>"full-width"]])
                ->add('user_username',FormType\TextType::class,[
                    "mapped"=>false,
                    "required"=>true,
                    "label"=>$this->trans("setupform.user.username.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["class"=>"full-width"]])
                ->add('user_email',FormType\EmailType::class,[
                    "mapped"=>false,
                    "required"=>true,
                    "label"=> $this->trans("setupform.user.email.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["class"=>"full-width"]])
                ->add("user_password0", FormType\PasswordType::class,[
                    "mapped"=>false,
                    "required"=>true,
                    "label"=>$this->trans("setupform.user.password.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["class"=>"full-width"]])
                ->add("user_password1",FormType\PasswordType::class,[
                    "mapped"=>false,
                    "required"=>true,
                    "label"=>$this->trans("setupform.user.confpasswd.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["class"=>"full-width"]])
                ->add('db_migrate', FormType\CheckboxType::class,[
                    "mapped"=>false,
                    "label"=>$this->trans("setupform.db.migrate.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["checked"=>true],
                    "required"=>false])
                ->add('db_create', FormType\CheckboxType::class,[
                    "mapped"=>false,
                    "label"=>$this->trans("setupform.db.create.label",domain:"mydevel.webroot.setup"),
                    "required"=>false,
                    "attr"=>["checked"=>false]])
                ->add("db_mkmigrations", FormType\CheckboxType::class,[
                    "mapped"=>false,
                    "label"=>$this->trans("setupform.db.mkmigrations.label",domain:"mydevel.webroot.setup"),
                    "required"=>false,
                    "attr"=>["checked"=>false]])
                ->add('db_backend',FormType\ChoiceType::class,[
                    "mapped" => false,
                    "label" => $this->trans("setupform.db.backend.label",domain:"mydevel.webroot.setup"),
                    "required" => true,
                    "choices" => [
                        $this->trans("setupform.db.backend.choices.sqlite",domain:"mydevel.webroot.setup")=>"sqlite",
                        $this->trans("setupform.db.backend.choices.mysql",domain:"mydevel.webroot.setup")=>"mysql",
                        $this->trans("setupform.db.backend.choices.portgesql",domain:"mydevel.webroot.setup")=>"postgresql",
                        $this->trans("setupform.db.backend.choices.url",domain:"mydevel.webroot.setup")=>"url",
                    ],
                    "attr"=>["class"=>"full-width"]])
                ->add('db_database',FormType\TextType::class,[
                    "mapped"=>false,
                    "required"=>true,
                    "label"=>$this->trans("setupform.db.database.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["class"=>"full-width"]])
                ->add('db_host', FormType\TextType::class,[
                    "mapped"=>false,
                    "required"=>false,
                    "label"=>$this->trans("setupform.db.host.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["class"=>"full-width"]])
                ->add('db_port', FormType\IntegerType::class,[
                    "mapped"=>false,
                    "required"=>false,
                    "label"=>$this->trans("setupform.db.port.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["class"=>"full-width"]])
                ->add('db_user',FormType\TextType::class,[
                    "mapped"=>false,
                    "required"=>false,
                    "label"=>$this->trans("setupform.db.user.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["class"=>"full-width"]])
                ->add('db_password',FormType\TextType::class,[
                    "mapped"=>false,
                    "required"=>false,
                    "label"=>$this->trans("setupform.db.password.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["class"=>"full-width"]])
                ->add('db_url',FormType\TextType::class,[
                    "mapped"=>false,
                    "label"=>$this->trans("setupform.db.url.label",domain:"mydevel.webroot.setup"),
                    "required"=>false,
                    "attr"=>["class"=>"full-width"]])
                ->add('email_backend', FormType\ChoiceType::class, [
                    "mapped"=>false,
                    "label" => $this->trans("setupform.email.backend.label",domain:"mydevel.webroot.setup"),
                    "required"=>true,
                    "choices"=> [
                        $this->trans("setupform.email.backend.choices.none",domain:"mydevel.webroot.setup")=>"none",
                        $this->trans("setupform.email.backend.choices.smtp",domain:"mydevel.webroot.setup")=>"smtp",
                        $this->trans("setupform.email.backend.choices.sendmail",domain:"mydevel.webroot.setup")=>"sendmail",
                        $this->trans("setupform.email.backend.choices.native",domain:"mydevel.webroot.setup")=>"native",
                        $this->trans("setupform.email.backend.choices.dsn",domain:"mydevel.webroot.setup")=>"dsn",
                    ],
                    "attr"=>["class"=>"full-width"]])                
                ->add('email_path',FormType\TextType::class,[
                    "mapped"=>false,
                    "required"=>false,
                    "label"=>$this->trans("setupform.email.path.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["class"=>"full-width"]])
                ->add('email_user',FormType\TextType::class,[
                    "mapped"=>false,
                    "required"=>false,
                    "label"=>$this->trans("setupform.email.user.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["class"=>"full-width"]])
                ->add('email_password', FormType\PasswordType::class,[
                    "mapped"=>false,
                    "required"=>false,
                    "label"=> $this->trans("setupform.email.password.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["class"=>"full-width"]])
                ->add('email_host',FormType\TextType::class,[
                    "mapped"=>false,
                    "required"=>false,
                    "label"=>$this->trans("setupform.email.host.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["class"=>"full-width"]])
                ->add('email_port',FormType\TextType::class,[
                    "mapped"=>false,
                    "required"=>false,
                    "label"=>$this->trans("setupform.email.smtp-port.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["class"=>"full-width"]])
                ->add('email_dsn',FormType\TextType::class,[
                    "mapped"=>false,
                    "required"=>false,
                    "label"=>$this->trans("setupform.email.dsn.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["class"=>"full-width"]])
                ->add('email_address', FormType\EmailType::class,[
                    "mapped"=>false,
                    "required"=>true,
                    "label"=>$this->trans("setupform.email.sender.label",domain:"mydevel.webroot.setup"),
                    "attr"=>["class"=>"full-width"]])
                ->add('submit', FormType\SubmitType::class,[
                    "label"=>$this->trans("setupform.submit",domain:"mydevel.webroot.setup")])
                ->getForm();
    }
    
    public function setSetupFormFromData(Form $form,array $values)
    {
        if (array_key_exists("env", $values)) {
            $form->get("env")->setData($values["env"]);
        }
        if (array_key_exists("locale",$values)) {
            $form->get("locale")->setData($values["locale"]);
        }
        if (array_key_exists("tempdir",$values)) {
            $form->get("locale")->setData($values["tempdir"]);
        }
        if (array_key_exists("site", $values)) {
            $site=$values["site"];
            if (array_key_exists("name", $site)) {
                $form->get("site_name")->setData($site["name"]);
            }
            if (array_key_exists("email",$site)) {
                $form->get("site_email")->setData($site["email"]);
            }
            if (array_key_exists("rootdir", $site)) {
                $form->get("site_rootdir")->setData($site["rootdir"]);
            }
        }
        if (array_key_exists("database",$values)) {
            $db = $values["database"];
            if (array_key_exists("backend",$values)) {
                $form->get("db_backend")->setData($db["backend"]);
            }
            if (array_key_exists("database", $db)) {
                $form->get("db_database")->setData($db["database"]);
            }
            if (array_key_exists("host", $db)) {
                $form->get("db_host")->setData($db["host"]);
            }
            if (array_key_exists("port",$db)) {
                $form->get("db_host")->setData($db["port"]);
            }
            if (array_key_exists("user",$db)) {
                $form->get("db_user")->setData($db["user"]);
            }
            if (array_key_exists("password", $db)) {
                $form->get("db_password")->setData($db["password"]);
            }
            if (array_key_exists("url",$db)) {
                $form->get("db_url")->setData($db["url"]);
            }
        }
        if (array_key_exists("user", $values)) {
            $user = $values["user"];
            if (array_key_exists("username", $user)) {
                $form->get("user_username")->setData($user["username"]);
            }
            if (array_key_exists("email",$user)) {
                $form->get("user_email")->setData($user["email"]);
            }
        }
        if (array_key_exists("email", $values)) {
            $email = $values["email"];
            if (array_key_exists("backend",$email)) {
                $form->get("email_backend")->setData($email["backend"]);
            }
            if (array_key_exists("path",$email)) {
                $form->get("email_path")->setData($email["path"]);
            }
            if (array_key_exists("user", $email)) {
                $form->get("email_user")->setData($email["user"]);
            }
            if (array_key_exists("password", $email)) {
                $form->get("email_password")->setData($email["password"]);
            }
            if (array_key_exists("host",$email)) {
                $form->get("email_host")->setData($email["host"]);
            }
            if (array_key_exists("port", $email)) {
                $form->get("email_port")->setData($email["port"]);
            }
            if (array_key_exists("dsn", $email)) {
                $form->get("email_dsn")->setData($email["dsn"]);
            }
            if (array_key_exists("address", $email)) {
                $form->get("email_address")->setData($email["address"]);
            }
        }
    }
    
    public function setSetupFormDefaultsFromEnv(Form $form)
    {
        if (getenv("LOCALE") && strlen(getenv("LOCALE"))) {
            $form->get('locale')->setData(getenv("LOCALE"));
        } elseif (getenv("LANG") && strlen(getenv("LANG"))) {
            $form->get('locale')->setData(getenv("LANG"));
        }
        if (getenv("WEBROOT_TEMP") && strlen(getenv("WEBROOT_TEMP"))) {
            $form->get('tempdir')->setData(getenv("WEBROOT_TEMP"));
        } elseif (getenv("TMP") && strlen(getenv("TMP"))) {
            $form->get('tempdir')->setData(join(DIRECTORY_SEPARATOR,[getenv("TMP"),"webroot"]));
        } elseif (getenv('TEMP') && strlen(getenv("TEMP"))) {
            $form->get("tempdir")->setData(join(DIRECTORY_SEPARATOR,[getenv("TEMP"),"webroot"]));
        } else {
            $form->get("tempdir")->setData(join(DIRECTORY_SEPARATOR,[$this->project_dir,"temp"]));
        }        
        if (getenv("CONTACT_EMAIL") && strlen(getenv("CONTACT_EMAIL"))) {
            $form->get('site_email')->setData(getenv("CONTACT_EMAIL"));
        }
        if (getenv("SITE_NAME") && strlen(getenv("SITE_NAME"))) {
            $form->get("site_name")->setData(getenv("SITE_NAME"));
        }
        if (getenv("SITE_ROOTDIR") && strlen("SITE_ROOTDIR")) {
            $form->get("site_rootdir")->setData(getenv("SITE_ROOTDIR"));
        }
        if (getenv("DATABASE_URL") && strlen("DATABASE_URL")) {
            $form->get("db_url")->setData(getenv("DATABASE_URL"));
            $form->get("db_backend")->setData("url");
        }
        if (getenv("MAILER_DSN") && strlen(getenv("MAILER_DSN"))) {
            $form->get("email_dsn")->setData(getenv("MAILER_DSN"));
            $form->get("email_backend")->setData("dsn");
        }
    }    
    
    public function getDataFromSetupForm(Form $form) : array
    {
        $data=[
            'env' => $form->get('env')->getNormData()
        ];
        
        $locale = $form->get('locale')->getNormData();
        if ($locale && strlen($locale)) {
            $data['locale'] = $locale;
        }
        
        $tempdir = $form->get('tempdir')->getNormData();
        if ($tempdir && strlen($tempdir)) {
            $data["tempdir"] = $tempdir;
        }
        
        $site=[
            "name" => $form->get("site_name")->getNormData(),
            "email" => $form->get("site_email")->getNormData(),
            "rootdir" => $form->get("site_rootdir")->getNormData(),
        ];
        $data["site"] = $site;
        
        $user = [];
        $tmp_user = new WebrootUser();
        
        $user_username = $form->get('user_username')->getNormData();
        if (!$user_username) {
            $user_username="";
        }
        $tmp_user->setUsername($user_username);
        $user['username']=$user_username;
        
        $user_email = $form->get('user_email')->getNormData();
        if (!$user_email) {
            $user_email = "";
        }
        $tmp_user->setEmail($user_email);
        $user['email'] = $user_email;
        
        $password0 = $form->get('user_password0')->getData();
        $password1 = $form->get('user_password1')->getData();
        if ((strlen($password0) >= 8) && ($password0 == $password1)) {
            $passwd = $this->password_hasher->hashPassword($tmp_user,$password0);
        }
        $user['password'] = $passwd;
        $tmp_user->setPassword($passwd);
        
        $data['user'] = $user;
        
        $db = array();
        
        $db["backend"] = $form->get("db_backend")->getNormData();
        $db["make_migrations"] = $form->get("db_mkmigrations")->getNormData();
        $db["migrate"] = $form->get("db_migrate")->getNormData();
        $db["create_database"] = $form->get("db_create")->getNormData();
        $db_url = $form->get('db_url')->getNormData();
        if ($db_url && strlen($db_url)) {
            $db['url'] = $db_url;
        }
        
        $db_database = $form->get('db_database')->getNormData();
        if ($db_database && strlen($db_database)) {
            $db["database"] = $db_database;
        }
        $db_host = $form->get('db_host')->getNormData();
        if ($db_host && strlen($db_host)) {
            $db['host'] = db_host;
        }
        $db_port = $form->get('db_port')->getNormData();
        if ($db_port &&  (int) $db_port) {
            $db['port'] = "db_port";
        }
        $db_user = $form->get('db_user')->getNormData();
        if ($db_user && strlen($db_user)) {
            $db["user"] = $db_user;
        }
        $db_password = $form->get('db_password')->getNormData();
        if ($db_password && strlen($db_password)) {
            $db['password'] = $db_password;
        }
        $data["database"] = $db;
        
        $email = [
            "backend" => $form->get("email_backend")->getNormData(),
            "address" => $form->get("email_address")->getNormData(),
        ];
        
        $email_path = $form->get("email_path")->getNormData();
        if ($email_path && strlen($email_path)) {
            $email["path"] = $email_path;
        }
        $email_user = $form->get("email_user")->getNormData();
        if ($email_user && strlen($email_user)) {
            $email["user"] = $email_user;
        }
        
        $email_password = $form->get("email_password")->getNormData();
        if ($email_password && strlen($email_password)) {
            $email["password"] = $email_password;
        }
        $email_host = $form->get('email_host')->getNormData();
        if ($email_host && strlen($email_host)) {
            $email["host"] = $email_host;
        }
        $email_port = $form->get('email_port')->getNormData();
        if ($email_port && (int) $email_port) {
            $email["port"] = $email_port;
        }
        $email_dsn = $form->get('email_dsn')->getNormData();
        if ($email_dsn && strlen($email_dsn)) {
            $email["dsn"] = $email_dsn;
        }
        $data["email"] = $email;
        if (count($data) == 0)  {
            throw \Exception("No Data!!!!");
        }
        
        $commands=[];
        if ($data["database"]["create_database"]) {
            $commands[] = "create-database";
        }
        if ($data["database"]["make_migrations"]) {
            $commands[] = "make-migrations";
        }
        if ($data["database"]["migrate"]) {
            $commands[] = "migrate";
        }
        $commands[] = "populate-database";
        
        $data["__commands__"] = $commands;
        
        return $data;
    }
    
    public function exportSetupFormToFile(Form $form,string $filename)
    {
        $file = fopen($filename,"w");
        fwrite($file, (new Yaml)->dump($this->getDataFromSetupForm($form)));
        fsync($file);
        fclose($file);
    }
    
    function generateRandomString($length = 10) : string
    {
        $x='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_,;.:()[]{}!$&/=?+*<>|@';
        $x_len=strlen($x);
        $ret="";
        
        for ($i=0; $i< $length; ++$i) {
            $ret .= $x[rand() % $x_len];
        }
        return $ret;
    }
    
    protected function setInitialSetupDataCookie(Request $request,Form $form,Response $response)
    {
        if ($this->hasInitialSetupDataCookie($request)) {
            $old_file = $request->cookies->get(WebrootSetupController::INITIAL_SETUP_COOKIE);
            if ($old_file && strlen($old_file) && file_exists($old_file)) {
                unlink($old_file);
            }
        }
        $tempdir = $form->get("tempdir")->getNormData();
        if (!file_exists($tempdir)) {
            mkdir($tempdir);
        }
        $filename = tempnam($tempdir,"wis");
        $cookie = new Cookie(
                WebrootSetupController::INITIAL_SETUP_COOKIE,
                $filename,
                0,
                "/setup/",
                $_SERVER["SERVER_NAME"],
                $request->getScheme() === "https",
                false,
                true,
                'Strict');
        $this->exportSetupFormToFile($form, $filename);
        $response->headers->setCookie($cookie);
    }
    
    protected function getDataFromInitialSetupDataCookie(Request $request): ?array
    {
        if (!$this->hasInitialSetupDataCookie($request)) {
            return null;
        }
        
        $data_cookie = $request->cookies->get(WebrootSetupController::INITIAL_SETUP_COOKIE);
        if (!file_exists($data_cookie)) {
            return null;
        }
        $data = (new Yaml)->parseFile($data_cookie);
        return $data;
    }
    
    protected function hasInitialSetupDataCookie(Request $request): bool
    {
        return $request->cookies->has(WebrootSetupController::INITIAL_SETUP_COOKIE);
    }
    protected function removeInitialSetupDataCookie(Request $request, Response $response) 
    {
        if ($this->hasInitialSetupDataCookie($request)) {
            $filename = $request->cookies->get(WebrootSetupController::INITIAL_SETUP_COOKIE);
            if (file_exists($filename)) {
                unlink($filename);
            }
            $response->headers->removeCookie(WebrootSetupController::INITIAL_SETUP_COOKIE,"/setup/");
        }
    }
    public function envencode(string $str): string
    {
        return preg_replace('/%/','%%',urlencode($str));
    }
    
    public function writeDotEnvLocal(array $data,bool $generate_app_secret=true) {
        
        
        $file = fopen(join(DIRECTORY_SEPARATOR,[$this->project_dir,".env.local"]),"w");
        fwrite($file,"APP_ENV=" . $data["env"] . "\n");
        
        if ($generate_app_secret) {
            fwrite($file,"APP_SECRET=\"" . $this->generateRandomString(60) . "\"\n");
        } else {
            fwrite($file,"APP_SECET=\"" . getenv("APP_SECRET") . "\"\n");
        }
        fwrite($file,"SITE_NAME=\"" . $data['site']['name'] . "\"\n");
        fwrite($file,"SITE_EMAIL=\"". $data['site']['email'] . "\"\n");
        fwrite($file,"SITE_ROOTDIR=\"" .$data['site']['rootdir'] ."\"\n");
        
        $db_backend=$data['database']['backend'];
        if ($db_backend === "sqlite") {
            fwrite($file,"DATABASE_URL=\"sqlite:///" . $data['database']['database'] . "\"\n");
        } elseif ($db_backend === "mysql") {
            fwrite($file,"DATABASE_URL=\"msysql://"
                    . urlencode($data['database']['user'])
                    . ":" . urlencode($data['database']['password'])
                    . "@" . urlencode($data['database']['host'])
                    . ":" . $data['database']['port']
                    . "/" . urlencode($data['database']['database'])
                    . "?charset=utf8mb4\"\n");
        } elseif ($db_backend === "postgresql") {
            fwrite($file,"DATABASE_URL=\"postgresql://"
                    . urlencode($data['database']['user'])
                    . ":" . urlencode($data['database']['password'])
                    . "@" . urlencode($data['database']['host'])
                    . ":" . $data['database']['port']
                    . "/" . urlencode($data['database']['database'])
                    . "?charset=utf8");
        } elseif ($db_backend === "url") {
            fwrite($file,"DATABASE_URL=\"" . $data["database"]["url"] . "\"\n");
        }
        
        $mail_backend = $data["email"]["backend"];
        if ($mail_backend === "none") {
            $MAILER_DSN="null://null";
        } elseif ($mail_backend === "smtp") {
            $MAILER_DSN="smtp://" 
                    . urlencode($data['email']['user'])
                    . ':' . urlencode($data['email']['password'])
                    . '@' . urlencode($data['email']['host'])
                    . ':' . $data['email']['port'];
        } elseif ($mail_backend === "sendmail") {
            $MAILER_DSN="sendmail://default";
        } elseif ($mail_backend === "native") {
            $MAILER_DSN="native://default";
        } elseif ($mail_backend === "dsn") {
            $MAILER_DSN=$data['email']['dsn'];
        }
        fwrite($file,"MAILER_DSN=\"" . $MAILER_DSN . "\"\n");
        
        fclose($file);
    }
    
    
    
    protected function addRoles()
    {
        foreach ($this->getInitialRoles(false) as $roledata) {
            $role = new WebrootRole();
            $role->setRole($roledata["role"]);
            $role->setName($roledata["name"]);
            $role->setDescription($roledata["description"]);
            $this->entitymanager->persist($role);
        }
        $this->entitymanager->flush();
    }
    
    protected function addAdministrator(array $setup_data)
    {
        $user = new WebrootUser();
        $user->setUsername($setup_data["user"]["username"]);
        $user->setEmail($setup_data["user"]["email"]);
        $user->setAdmin(true);
        $user->setRoles(["ROLE_SUPERADMIN","ROLE_ADMIN","ROLE_USER"]);
        $user->setPassword($setup_data["user"]["password"]);
        $this->entitymanager->persist($user);
        $this->entitymanager->flush();
    }
    
    protected function addRootDir(array $setup_data) {
        $admin = $this->entitymanager
                ->getRepository(WebrootUser::class)
                ->findByUsername($setup_data['user']['username']);
        $wrf = new WebrootFile();
        $wrf->setUrl($this->generateUrl("app_webroot"));
        $wrf->setAbspath($setup_data["site"]["rootdir"]);
        $wrf->setOwner($admin);
        $this->entitymanager->persist($wrf);
        
        $superadmin_role = $this->entitymanager
                ->getRepository(WebrootRole::class)
                ->findByRolename("ROLE_SUPERADMIN");
        
        $rootperm = new WebrootFilePermission();
        $rootperm->setWebrootFile($wrf);
        $rootperm->setRole($superadmin_role);
        $rootperm->setDeleteable(true);
        $rootperm->setReadable(true);
        $rootperm->setWriteable(true);
        $this->entitymanager->persist($rootperm);
        
        $admin_role = $this->entitymanager
                ->getRepository(WebrootRole::class)
                ->findByRolename("ROLE_ADMIN");
        $adminperm = new WebrootFilePermission();
        $adminperm->setWebrootFile($wrf);
        $adminperm->setRole($admin_role);
        $adminperm->setReadable(true);
        $adminperm->setWriteable(false);
        $adminperm->setDeleteable(false);
        $this->entitymanager->persist($adminperm);
        
        $this->entitymanager->flush();
    }
}
