<?php

namespace App\Services;

use PDO;
use App\Models\Group;
use App\Models\DataConnection;
use App\Models\InstalledVersion;
use App\Models\ModulePermission;
use App\Models\ResourcePermission;
use Illuminate\Support\Facades\DB;
use Illuminate\Encryption\Encrypter;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Support\Facades\Config;
use App\Repositories\DataConnectionRepository;
use Illuminate\Database\Connectors\ConnectionFactory;

class DataConnectionService
{
    public $dataConnectionRepository;

    public function __construct(DataConnectionRepository $dataConnectionRepository = null)
    {
        $this->dataConnectionRepository = $dataConnectionRepository;
    }

    public function getInstalledVersionDate()
    {
        return InstalledVersion::where('is_current', 1)
            ->first()->installation_date;
    }

    public function getAllConnections()
    {
        return DataConnection::orderBy('created_at', 'desc')->get();
    }

    public function getDefaultConnection()
    {
        try {
            $prefix =  config("srm_config.installer.table_prefix", "srm_");

            $defaultConnection = config("srm_db_config");

            $ConnectionConfig = [
                'driver' => strtolower($defaultConnection["driver"]),
                'host' => $defaultConnection["host"] . ":" . $defaultConnection["port"],
                'database' => $defaultConnection["database"],
                'username' => $defaultConnection["username"],
                'password' => $defaultConnection["password"],
                'port' => $defaultConnection["port"],
                'options' => [PDO::ATTR_PERSISTENT => true]
            ];

            $factory = new ConnectionFactory(app());
            $connection = $factory->make($ConnectionConfig);

            return $connection;

        } catch(\Exception $e) {
            return $e->getMessage();
        }

    }

    public function getConnection($connection)
    {
        if($connection == "default") {
            return $this->getDefaultConnection();
        } else {
            return $this->getNonDefaultConnection($connection);
        }
    }

    public function getNonDefaultConnection($connection)
    {
        $connection = DataConnection::find($connection);

        try {
            $connectionParameters = $this->getConnectionAttributes(decrypt($connection->connection_string));
            $ConnectionConfig = [
                'driver' => strtolower($connection->db_type),
                'host' => $connection->server . ":" . $connectionParameters["port"],
                'database' => $connection->db,
                'username' => $connectionParameters["user"],
                'password' => $connectionParameters["password"],
                'port' => $connectionParameters["port"],
                'options' => [PDO::ATTR_PERSISTENT => true]

            ];
            $factory = new ConnectionFactory(app());
            $connection = $factory->make($ConnectionConfig);


            return $connection;

        } catch(\Exception $e) {
            // dd('asasas');
            return false;
        }

    }

    private function getConnectionAttributes($connectionString)
    {
        $parameters = explode(';', $connectionString);

        $connectionArray = [];

        foreach ($parameters as $parameter) {
            list($key, $value) = explode('=', $parameter, 2);

            $connectionArray[$key] = $value;
        }

        return $connectionArray;
    }


    public function getAllDatabases()
    {
        return $this->getAllConnections()->unique("db")->pluck("db");
    }

    public function getAllServers()
    {
        $all_servers = $this->getAllConnections()
            ->where('server', '!=', 'localhost')
            ->unique("server")
            ->pluck("server");

        if (
            config('srm_db_config') &&
            config('srm_db_config.host') !== 'localhost'
        ) {

            $all_servers->push(
                config('srm_db_config.host')
            );

            return $all_servers->unique();
        }

        return $all_servers;
    }

    public function store($data)
    {
        $this->testConnection($data);

        $connection_string = $this->encryptConnectionString($data);

        $this->dataConnectionRepository->store($data, $connection_string);
    }

    public function update($data, $connection)
    {
        $this->testConnection($data);

        $connection_string = $this->encryptConnectionString($data);

        $this->dataConnectionRepository->update($data, $connection_string, $connection);
    }


    public function destory($connection)
    {
        if ($connection->resources()->exists()) {

            abort(400, "The deletion of \"{$connection->connection_name}\" data connection is not possible as it currently being used by some analytics resources.");
        } else {

            $connection->delete();

            return "\"{$connection->connection_name}\" data connection has been successfully deleted!";
        }
    }

    public function testConnection($data)
    {
        $port = isset($data["port"]) ? $data["port"] : "3306";
        $charset = isset($data["charset"]) ? $data["charset"] : "utf8mb4";
        $collation = isset($data["collation"]) ? $data["collation"] : "utf8mb4_unicode_ci";
        $ConnectionConfig = [
            'driver' => strtolower($data["database_type"]),
            'host' => $data["server"] . ":$port",
            'database' => $data["database"],
            'username' => $data["username"],
            'password' => $data["password"],
            'port' => $port,
            'charset' => $charset,
            'collation' => $collation
        ];

        try {
            $factory = new ConnectionFactory(app());

            $connection = $factory->make($ConnectionConfig);
            $connection->getPdo();
        } catch (\Exception $e) {
            abort(400, "Connection failed: " . $e->getMessage());
        }
    }


    private function encryptConnectionString($data)
    {
        return encrypt($this->buildConnectionString($data));
    }

    private function buildConnectionString($data)
    {
        switch (strtolower($data["database_type"])) {
            case 'mysql':
                return $this->buildMySQLConnectionString($data);
                break;
            case 'postgresql':
                break;
        }
    }

    private function buildMySQLConnectionString($data)
    {
        $port = isset($data["port"]) ? $data["port"] : "3306";
        $charset = isset($data["charset"]) ? $data["charset"] : "utf8mb4";
        $collation = isset($data["collation"]) ? $data["collation"] : "utf8mb4_unicode_ci";

        return sprintf(
            'user=%s;password=%s;charset=%s;collation=%s;port=%s',
            $data['username'],
            $data['password'],
            $charset,
            $collation,
            $port,
        );
    }
}