<?php

namespace Application\Service\Extension\CheckLogic;

use Application\Controller\LogicController;
use Application\Service\DB\Dictionary\Form;
use Application\Service\DB\Dictionary\FormGroup;
use Application\Service\DB\Dictionary\ItemForm;
use Application\Service\Exceptions\InvalidArgumentException;
use Application\Service\Extension\Helper\StringHelper;
use Application\Service\Extension\Laminas;

/**
 * 表达式生成器
 */
class ExpressionGenerator
{
    /**
     * 使用了哪些变量
     * @var array
     */
    public array $usingVars = [];

    /**
     * 涉及到了哪些表单
     * @var array
     */
    public array $usingForms = [];

    public array $usingCheckTime = [];

    public ?string $expression = null;

    public ?string $originExpression = null;

    public ?string $expressHash = null;

    public string $note = '';

    public array $pointer = [];

    public ?int $itemId = null;

    public bool $hasMulti = false;

    /**
     * 一对多的表单类型
     * @see FormGroup
     * @var int|null
     */
    public ?int $multiGroupId = 0;

    public ?int $multiFormId = null;

    public ?int $multiFormCheckTimeId = null;

    public ?bool $isProcess = false;


    public static function formatVar($var)
    {
        return str_replace([CheckLogicApplication::VAR_MARKS_LEFT, CheckLogicApplication::VAR_MARKS_RIGHT], '', $var);
    }

    public function setExpression(string $ex): ExpressionGenerator
    {
        $this->expression = $ex;
        $this->originExpression = $ex;
        return $this;
    }

    public function setLogicHash(string $hash): ExpressionGenerator
    {
        $this->expressHash = $hash;
        return $this;
    }

    public function setItemId(int $itemId): ExpressionGenerator
    {
        $this->itemId = $itemId;
        return $this;
    }

    public function setNote(string $note): ExpressionGenerator
    {
        $this->note = $note;
        return $this;
    }

    public function setPointer(string $pointer): ExpressionGenerator
    {
        $pointer = StringHelper::jsonDecode($pointer);
        $this->pointer = $pointer;
        foreach ($pointer as $item) {
            $this->usingVars[] = $item[2] == 'OCR_CHECK_TIME' ? 'OCR_CHECK_TIME' : Laminas::$serviceManager->itemFormField->fetchOne([
                'where' => [
                    'var_name' => $item[2],
                    'item_id' => $this->itemId
                ]
            ])['id'];

            $this->usingCheckTime[] = Laminas::$serviceManager->itemChecktime->fetchOne([
                'where' => [
                    'var_name' => $item[0],
                    'item_id' => $this->itemId
                ]
            ])['id'];

            $this->usingForms[] = Laminas::$serviceManager->itemForm->fetchOne([
                'where' => [
                    'var_name' => $item[1],
                    'item_id' => $this->itemId
                ]
            ])['id'];
        }

        return $this;
    }

    /**
     * 生成表达式
     * @return string
     */
    public function generate(): string
    {
        $this->validExpression($this->expression);
// 处理函数
//        preg_match_all('/(_val)\(.*?\`.*?\`?\)/', $logicExpression, $functions);
        $pattern = CheckLogicApplication::formatPattern();
        // 处理函数
        preg_match_all($pattern, $this->expression, $vars);

        foreach ($vars[0] ?? [] as $k => $var) {
            $formatVar = self::formatVar($var);
            // 不同检查点
            if (stripos($var, '.') !== false) {
                list($checkTime, $formVar, $varName) = explode('.', $formatVar);
                $checkTimeVarName = $checkTime;
                // $checkTime 是检查点标识(V1, V2等)
                // AE需要特殊处理一下
                $checkTime = ($checkTime == 'AE' ? 'AE' : $this->handleParams($checkTime, true));
                if (!$checkTime) {
                    throw new InvalidLogicExpressionException('检查点有误呢');
                }

                $this->setIsMulti($varName, $checkTime);
                $r = "_visit('{$checkTime}','{$formVar}','{$varName}')";
                $this->expression = str_replace($var, $r, $this->expression);
            } else {
                // 好像没用了
            }
        }

        preg_match_all('/(_val|_empty|_isChecked|_outAnchorTime)\(.*?\)/', $this->expression, $vals);

        foreach ($vals[0] ?? [] as $k => $v) {
            // ae表单
            if (stripos($v, '_ae') !== false) {
                $this->hasMulti = true;
                $this->multiGroupId = FormGroup::AE;
            }

            $replaceFuncEx = $this->addFunctionArgsMarks($v);
            $this->expression = str_replace($v, $replaceFuncEx, $this->expression);
        }

        return str_replace(array_keys(CheckLogicApplication::CONDITIONS), array_values(CheckLogicApplication::CONDITIONS), $this->expression);
    }

    private function handleParams(string $params, bool $isCheckTimeVarName = false): string
    {
        if ($isCheckTimeVarName === true) {
            return $this->getCheckTimeId($params);
        }

        $ex = explode('.', $params);
        // 正常参数, `_val(2020-01-01)`
        if (count($ex) == 1) {
            return $params;
        }
        // 检查点变量 `V1.xxx.xxx`
        list($checkTime, $formVarName, $fieldVarName) = explode('.', $params);
        if ($checkTime === 'AE') {
            return $params;
        }

        return "{$this->getCheckTimeId($checkTime)}.{$formVarName}.{$fieldVarName}";
    }

    private function getCheckTimeId(string $checkTimeVarName)
    {
        return Laminas::$serviceManager->itemChecktime->fetchOne([
                'where' => ['item_id' => $this->itemId, 'var_name' => $checkTimeVarName]
        ])['id'] ?? $checkTimeVarName;
    }

    private function setIsMulti(string $varName, $checkTime)
    {
        $fieldData = Laminas::$serviceManager->itemFormField->fetchOne([
            'where' => ['var_name' => $varName, 'item_id' => $this->itemId]
        ]);

        $formData = Laminas::$serviceManager->itemForm->fetchOne([
            'where' => [
                'id' => $fieldData['form_id']
            ]
        ]);
        if ($formData['type'] == Form::FORM_TYPE_MULTI) {
            $this->multiFormId = $fieldData['form_id'];
            $this->multiFormCheckTimeId = $checkTime == 'AE' ? 0 : $checkTime;
            $this->hasMulti = true;
            $this->multiGroupId = $formData['group_id'];
        }

        // 贯穿全程
        if ($formData['group_id'] == FormGroup::THROUGH_PROCESS) {
            $this->isProcess = true;
        }
    }

    public function setCheckTime(string $checkTime)
    {
        $this->usingCheckTime[] = $checkTime;
    }

    /**
     * 为逻辑核查公式里的内置函数添加参数符号
     * `_date(2020-01-01)` => `_date('2020-01-01')`
     * @param string $expression
     * @return string
     */
    private function addFunctionArgsMarks(string $expression): string
    {
        preg_match_all('/\((.*)?\)/', $expression, $functionArgs);
        foreach ($functionArgs[1] as $arg) {
            if (stripos($arg, ',') !== false) {
                foreach (explode(',', $arg) as $params) {
                    // 给所有参数加个''
                    $expression = strtr($expression, [
                        $params => '\'' . trim($this->handleParams($params)) . '\''
                    ]);
                }
            } else {
                $expression = strtr($expression, [
                    $arg => $this->handleParams($arg)
                ]);
            }
        }
        return $expression;
    }

    /**
     * 验证逻辑核查公式
     * @param string $logicExpression
     */
    private function validExpression(string $logicExpression)
    {
        // 验证括号
        $this->validParenthesis($logicExpression);
    }

    /**
     * 检查公式括号是否正确
     * @param $logicExpression
     * @return void
     */
    private function validParenthesis($logicExpression): void
    {
        if (empty($logicExpression)) {
            throw new InvalidLogicExpressionException('表达式不能为空');
        }

        if (strpos($logicExpression, '(' ) === false && strpos($logicExpression, ')' ) === false) {
            return;
        }

        $logicExpression_len = strlen($logicExpression);
        $j = $z = 0;
        for ($i=0; $i < $logicExpression_len; $i++) {
            if ($logicExpression[$i] == '(') {
                $j++;
            }
            if ($logicExpression[$i] == ')') {
                if ($j > 0) {
                    $j--;
                } else {
                    $z = 1;
                }
            }
        }
        if (empty($j) && empty($z)) {
            return;
        }
        
        throw new InvalidLogicExpressionException('逻辑核查公式有误。');
    }

    /**
     * @param string $formVarName
     * @return mixed
     */
    private function getFormId(string $formVarName)
    {
        $formId = Laminas::$serviceManager->itemForm->fetchOne([
            'where' => ['var_name' => $formVarName]
        ])['id'] ?? false;

        if (!$formId) {
            throw new InvalidArgumentException('非法的变量名。');
        }

        return $formId;
    }
}