<?php

namespace Application\Service\Extension\CheckLogic\plugins;

use Application\Service\DB\Dictionary\Form;
use Application\Service\DB\Dictionary\FormGroup;
use Application\Service\Extension\CheckLogic\CheckLogicApplication;
use Application\Service\Extension\CheckLogic\InvalidValException;
use Application\Service\Extension\Helper\ArrayHelper;
use Application\Service\Extension\Helper\StringHelper;
use Application\Service\Extension\Laminas;
use Laminas\Db\Sql\Predicate\Like;
use Laminas\Db\Sql\Predicate\Predicate;

class Required
{
    public static array $isExecutePatient = [];

    private int $itemId = 0;
    private int $signId = 0;
    private int $patientId = 0;
    private int $checkTimeId = 0;

    private bool $isAe = false;

    private ?CheckLogicApplication $logicApp = null;

    public function setParams($params = []): Required
    {
        foreach ($params as $key => $param) {
            if (property_exists($this, $key)) {
                $this->{$key} = $param;
            }
        }
        return $this;
    }

    public function execute()
    {
        CheckLogicApplication::debugPrint('run required plugin core.');
        // 执行过就不要执行了, 一个受试者只运行一次。
        if ($this->isExecute()) {
            return;
        }

        // 匹配所有带必填选项的
        $query = Laminas::$serviceManager->itemFormField->fetchAll([
            'where' => [
                'item_id' => $this->itemId,
                new Like('rules', '%"v-required";s:1:"1"%') // 匹配必填的反序列化规则
            ]
        ]);

        // 哪些是必填的表单
        $requiredForms = array_unique(ArrayHelper::getColumn($query, 'form_id'));

        // 哪些是必填的字段
        $requiredFields = ArrayHelper::index($query, null,'form_id');
        foreach ($requiredFields as &$field) {
            $field = ArrayHelper::index($field, 'id');
        }

        foreach ($requiredForms as $formId) {
            $this->logicApp->getFieldType($formId);
            // TODO: cache
            $form = Laminas::$serviceManager->itemForm->fetchOne([
                'where' => ['id' => $formId]
            ]);

            if ($form['group_id'] == FormGroup::AE) {
                $contents = $this->getContents($formId, true);
            } else {
                $contents = $this->getContents($formId);
            }
            $this->handleContents($contents, $requiredFields[$formId], $formId);
        }
    }

    /**
     * 是否已经执行过此受试者
     * @return bool
     */
    private function isExecute(): bool
    {
        return isset(self::$isExecutePatient[$this->patientId]);
    }

    private function handleContents($contents, array $requiredFields = [], $formId)
    {
        foreach ($contents as $content) {
            $data = StringHelper::jsonDecode($content['data']);
            foreach ($requiredFields as $field => $item) {
              $callable = function () use ($field, $data, $formId) {
                  if ((isset($data[$field]) && !$data[$field]) || (!isset($data[$field]))) {
                      throw new InvalidValException("{$this->logicApp->getFieldType($formId)[$field]['name']} 不能为空。");
                  }
              };
                try {
                    $callable();
                } catch (InvalidValException $exception) {
                    $this->setLogicError($exception, $content, $field);
                }
            }
        }
    }

    private function hasError($contentId, $field)
    {
        return Laminas::$serviceManager->itemPatientFormLogicError->fetchOne([
            'where' => [
                'patient_id' => $this->patientId,
                'item_id' => $this->itemId,
                'error_expression' => '系统内置核查',
                'expression_id' => 0,
                'multi_id' => $contentId,
                'sign_id' => $this->signId,
                'is_queried' => 1,
                'error_type' => 0,
                'is_closed' => 0,
                'is_ae' => intval($this->isAe),
                'field' => $field
            ]
        ])['id'] ?? false;
    }

    /**
     * 写入逻辑核查错误表
     * @param $exception
     * @param $content
     * @param $field
     */
    private function setLogicError($exception, $content, $field)
    {
        $conditions = [
            'patient_id' => $this->patientId,
            'item_id' => $this->itemId,
            'error_expression' => '系统内置核查',
            'expression_id' => 0,
            'is_exception' => 0,
            'expression_note' => $exception->getMessage(),
            'is_del' => 0,
            'multi_id' => $content['id'],
            'sign_id' => $this->signId,
            'is_queried' => 1,
            'checktime_id' => $content['checktime_id'],
            'error_type' => 0,
            'is_closed' => 0,
            'is_ae' => intval($this->isAe),
            'field' => $field,
            'is_plugin' => self::class,
            'form_id' => $content['form_id']
        ];
        if ($errorId = $this->hasError($content['id'], $field)) {
            $conditions['id'] = $errorId;
        }

        $errorId = Laminas::$serviceManager->itemPatientFormLogicError->save($conditions);

        if (isset($conditions['id'])) {
            $errorId = $conditions['id'];
        }

        // 直接发送到sdm
        Laminas::$serviceManager->itemPatientFormLogicErrorQuery->save([
            'content' => $exception->getMessage(),
            'error_id' => $errorId,
            'platform' => Laminas::$serviceManager->itemPatientFormLogicErrorQuery::PLATFORM_EDC,
            'is_reply' => 0
        ]);

        $this->setPointer($errorId, $content, $field, $exception->getMessage());
    }

    private function setPointer($errorId, $content, $field, $errorMessage)
    {
        $condition = [
            'patient_id' => $this->patientId,
            'checktime_id' => $content['checktime_id'],
            'form_id' => $content['form_id'],
            'item_id' => $this->itemId,
            'is_del' => 0,
            'logic_expression_id' => 0,
            'error_id' => $errorId,
            'prop' => $field,
            'error_message' => $errorMessage,
            'error_type' => 0
        ];

        if ($has = Laminas::$serviceManager->itemPatientFormLogicErrorPointer->fetchOne([
            'where' => [
                'error_id' => $errorId,
                'prop' => $field
            ]
        ])) {
            $condition['id'] = $has['id'];
        }

        Laminas::$serviceManager->itemPatientFormLogicErrorPointer->save($condition);
    }

    private function getContents(int $formId, bool $isAe = false)
    {
        $this->isAe = false;
        if ($isAe === true) {
            $this->isAe = true;
            $table = Laminas::$serviceManager->itemCsaeRelation;
            $aeRelation = $table->fetchCol('content_id', [
                'patient_id' => $this->patientId,
                'is_del' => 0
            ]);

            $data = Laminas::$serviceManager->itemPatientAeContent->fetchAll([
                'where' => ['id' => $aeRelation]
            ]);
        } else {
            $data = Laminas::$serviceManager->patientFormContent->fetchAll([
                'where' => ['patient_id' => $this->patientId, 'form_id' => $formId , 'is_del' => 0]
            ]);
        }

        return $data;
    }
}