<?php
namespace App\Http\Controllers;

use Illuminate\Http\Request;
use Illuminate\Contracts\Validation\Validator;

use App\Http\Requests;
use App\Http\Controllers\Controller;
use Config;
use DB;
use Exception;
use Log;

class SQLEnumController extends Controller
{
    private function buildSelectStatement(Request $request, $field, $config, $datas, $schema)
    {
        $vals = [];
        if (isset($schema->adv_config->filter) && isset($schema->adv_config->filter->$field))
            $vals[] = $schema->adv_config->filter->$field;

        foreach ($datas as $data) {
            $val = null;
            if (is_array($data) && isset($data[$field])) {
                $val = $data[$field];
            } else if (isset($data->$field)) {
                $val = $data->$field;
            }

            if (is_array($val)) {
                foreach ($val as $v)
                    $vals[] = $v;
            } else if ($val !== null) {
                $vals[] = $val;
            }
        }

        if (count($vals) == 0)
            return [null, null];

        $sql = preg_replace('/{[^\}]+}/', implode(',', array_fill(0, count($vals), '?')), $config->mapSql);
        return [$sql, $vals];
    }

    private function buildUnionStatement(Request $request, $field, $config, $datas, $schema)
    {
        $sql  = $config->sql;
        $vals = [];
        $sqls = [];

        preg_match_all('/{([^\}]+)}/', $sql, $fields);
        $sql = preg_replace('/{[^\}]+}/', '?', $sql);

        if ($sql != $config->sql) {
            foreach ($datas as $data) {
                foreach ($fields[1] as $f) {
                    if (is_array($data))
                        $vals[] = $data[$f];
                    else
                        $vals[] = $data->$f;
                }

                $sqls[] = $sql;
            }

            $sql = implode(' UNION ALL ', $sqls);
        }

        return [$sql, $vals];
    }

    private function fillEnumsData(Request $request, $field, $schema, $datas)
    {
        $config = $schema->adv_config->sqlDicts->$field;
        // 不需要转换
        if ($config->type == 0)
            return true;

        // datas是object(数据库接口)或则是array(history接口转换过来的), 所以对取值需要兼容
        // 对于history接口的数据,field对应的字段可能不存在
        // (数据库增加了字段,老的历史数据会不存在这个字段)
        $host   = $config->host;
        $port   = $config->port;
        $user   = $config->user;
        $passwd = $config->passwd;
        $dbname = $config->dbname;
        $sql    = '';
        $vals   = [];

        if (isset($config->mapSql)) {
            list($sql, $vals) = $this->buildSelectStatement($request, $field, $config, $datas, $schema);
        } else {
            list($sql, $vals) = $this->buildUnionStatement($request, $field, $config, $datas, $schema);
        }

        if ($sql == null)
            return true;

        $confname = $host . ':' . $port;

        $conn = [
            'driver'    => 'mysql',
            'host'      => $host,
            'port'      => $port,
            'database'  => $dbname,
            'username'  => $user,
            'password'  => $passwd,
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix'    => '',
        ];

        Config::set('database.connections.' . $confname, $conn);
        $result = DB::connection($confname)->select($sql, $vals);

        $out = [];
        $ret = [];
        if (count($result) == 0)
            return $ret;
        if (count((array)($result[0])) == 1)
            return $ret;

        foreach ($result as $item) {
            $arr = [];
            foreach ($item as $k => $v) {
                $arr[] = $v;
                if (count($arr) == 2)
                    break;
            }

            $out[$arr[0]] = $arr[1];
        }

        foreach ($datas as $data) {
            $val = is_array($data) ? (isset($data[$field]) ? $data[$field] : "") : $data->$field;
            if (is_array($val)) {
                foreach ($val as $v) {
                    if (isset($out[$v]))
                        $ret[$v] = $out[$v];
                    else
                        $ret[$v] = $v;
                }
            } else {
                if (isset($out[$val]))
                    $ret[$val] = $out[$val];
                else
                    $ret[$val] = $val;
            }
        }

        if (isset($schema->adv_config->filter) && isset($schema->adv_config->filter->$field)) {
            $v = $schema->adv_config->filter->$field;
            if (isset($out[$v]))
                $ret[$v] = $out[$v];
            else
                $ret[$v] = $v;
        }

        return $ret;
    }

    public function getEnumsByData(Request $request, $schema, $datas)
    {
        if (!isset($schema->adv_config) || !isset($schema->adv_config->sqlDicts))
            return [];

        $ret = [];
        foreach ($schema->adv_config->sqlDicts as $f => $config)
            $ret[$f] = $this->fillEnumsData($request, $f, $schema, $datas);

        return $ret;
    }

    public function getEnumsByQuery(Request $request)
    {
        $ret = $this->getEnums($request);
        if ($ret['errcode'] != 0)
            return ['q' => $request->input('q', ''), 'results' => []];

        $result = [];
        foreach ($ret['data'] as $data) {
            $result[] = ['id' => $data['key'], 'text' => $data['name']];
        }

        return ['q' => $request->input('q', ''), 'results' => $result];
    }

    public function getEnums(Request $request)
    {
        $host   = $request->input('host', '');
        $port   = $request->input('port', '');
        $user   = $request->input('user', '');
        $passwd = $request->input('passwd', '');
        $dbname = $request->input('dbname', '');
        $sql    = $request->input('sql', '');
        $type   = $request->input('type', 0);

        if (empty($host) || empty($port) || empty($user) || empty($passwd) || empty($dbname) || empty($sql))
            return ["errcode" => 404, "errmsg" => "Bad request"];

        $vals   = [];
        $q = null;
        if (preg_match_all('/{([^\}]+)}/', $sql, $fields) > 0) {
            foreach ($fields[1] as $f) {
                if ($f == 'user.email') {
                    $vals[] = $request->user->email;
                } else if ($f == 'user.userId') {
                    $vals[] = $request->user->userId;
                } else {
                    if ($f == 'request.q') {
                        $q = $request->input('q', '');
                        if ($q == '')
                            return ['errcode' => 404, "errmsg" => "Bad request"];
                        $q = str_replace(['%', '_', '\'', '"', ';'], ['\%', '\_', '', '', ''], $q);
                        continue;
                    }

                    $v = $request->input($f, '');
                    if (strlen($v) == 0)
                        return ["errcode" => 404, "errmsg" => "Bad request: {$f}的值为空"];
                    $vals[] = $v;
                }
            }
        }

        if (!empty($q))
            $sql = preg_replace('/{request.q}/', $q, $sql);
        $sql = preg_replace('/{[^\}]+}/', '?', $sql);

        $confname = $host . ':' . $port;

        $conn = [
            'driver'    => 'mysql',
            'host'      => $host,
            'port'      => $port,
            'database'  => $dbname,
            'username'  => $user,
            'password'  => $passwd,
            'charset'   => 'utf8',
            'collation' => 'utf8_unicode_ci',
            'prefix'    => '',
        ];

        Config::set('database.connections.' . $confname, $conn);

        $result = DB::connection($confname)->select($sql, $vals);

        $out = [];
        foreach ($result as $item) {
            $arr = [];
            foreach ($item as $k => $v) {
                $arr[] = $v;
                if ($type != 0 || count($arr) == 2)
                    break;
            }

            if (count($arr) == 1) {
                $arr[] = $arr[0];
            } else if (!empty($q)) {
                $arr[1] = $arr[1] . '(' . $arr[0] . ')';
            }

            $out[] = [ 'key' => $arr[0], 'name' => $arr[1] ];
        }

        return ['errcode' => 0, 'data' => $out];
    }
};