<?php

namespace Application\Service\Extension;

use Application\Common\Crypt as sdmCrypt;
use Application\Service\Exceptions\DbException;
use Application\Service\Extension\Helper\ArrayHelper;
use Application\Service\Extension\Helper\StringHelper;
use Laminas\Http\Response;
use Laminas\Http\Response as HttpResponse;
use Laminas\Mvc\Application;
use Laminas\Mvc\MvcEvent;
use Laminas\Mvc\View\Http\ExceptionStrategy;
use Laminas\View\Model\JsonModel;
use Laminas\View\Model\ViewModel;

class ErrorHandler extends ExceptionStrategy
{
    public function prepareExceptionViewModel(MvcEvent $e)
    {
        // Do nothing if no error in the event
        $error = $e->getError();
        if (empty($error)) {
            return;
        }

        // Do nothing if the result is a response object
        $result = $e->getResult();
        if ($result instanceof \Zend\Stdlib\ResponseInterface) {
            return;
        }

        switch ($error) {
            case Application::ERROR_CONTROLLER_NOT_FOUND:
            case Application::ERROR_CONTROLLER_INVALID:
            case Application::ERROR_ROUTER_NO_MATCH:
                // Specifically not handling these
                return;

            case Application::ERROR_EXCEPTION:
                $this->handlerError($e);
                break;
            default:
                $model = new ViewModel([
                    'message'            => 'An error occurred during execution; please try again later.',
                    'exception'          => $e->getParam('exception'),
                    'display_exceptions' => $this->displayExceptions(),
                ]);
                $model->setTemplate($this->getExceptionTemplate());
                $e->setResult($model);

                $response = $e->getResponse();
                if (! $response) {
                    $response = new HttpResponse();
                    $response->setStatusCode(500);
                    $e->setResponse($response);
                } else {
                    $statusCode = $response->getStatusCode();
                    if ($statusCode === 200) {
                        $response->setStatusCode(500);
                    }
                }

                break;
        }
    }

    private function handlerError(MvcEvent $e)
    {
        /** @var \Exception $exception */
        $exception = $e->getParam('exception');
        $traceId = uniqid('', true);
        // 如果不是逻辑错误
        if (!$exception instanceof \LogicException) {
            $errorData = [
                'type' => get_class($exception),
                'file' => method_exists($exception, 'getFile') ? $exception->getFile() : '',
                'errorMessage' => $exception->getMessage(),
                'executeSql' => method_exists($exception, 'getExecuteSql') ? $exception->getExecuteSql() : null,
                'line' => $exception->getLine(),
                'stack-trace' => explode("\n", $exception->getTraceAsString()),
                'identity' => $this->getIdentityData()
            ];

            $model = new JsonModel(ArrayHelper::merge([
                'code' => 500,
                'msg' => '很遗憾, 我们发生了一个错误。请将错误码发给我们, 以便进行错误排查。错误码:' . $traceId,
            ], call_user_func(function () use (&$errorData, &$e) {
                if ($e->getApplication()->getServiceManager()->get('config')['exception']['throw_exceptions'] ?? false) {
                    return [
                        'exception' => array_filter($errorData),
                    ];
                }
                return [];
            })));

            // 异常记录
            self::log2txt([
                'trace_id' => $traceId,
                'requestString' => $e->getRequest()->toString(),
                'exception' => $errorData
            ]);
        } else {
            $model = new JsonModel([
                'code' => $exception->getCode(),
                'msg' => $exception->getMessage(),
            ]);
        }

        $model->setTemplate($this->getExceptionTemplate());
        $e->setResult($model);
        $response = $e->getResponse();

        if (!$response) {
            $response = new HttpResponse();
            $response->setStatusCode(500);
            $e->setResponse($response);
        } else {
            // 强制设置状态码为200
            $response->setStatusCode(200);
        }
    }

    /**
     * @param $values
     * @param string $dir
     * @return false|int
     */
    public static function log2txt($values, $dir = 'lyj', &$taskData = [])
    {
        try {

            $msgData = [];
            $curDate = date('Y-m-d');
            $curTime = date('H:i:s');


            // 如果数据是数组则转换成字符
            if (is_array($values)) {
                if (isset($values['trace_id']) && !$values['trace_id']) {
                    return '';
                }

                $trace_id = $values['trace_id'];

                $values['datetime'] = $curDate.' '.$curTime;
                $msgData = [
                    'title' => "【{$_SERVER['HTTP_HOST']}】发生了致命错误：{$trace_id}",
                    'description' => "{$values['exception']['errorMessage']}",
                    'url' => "{$_SERVER['REQUEST_SCHEME']}://{$_SERVER['HTTP_HOST']}/common/errormsg?code=".rawurlencode($trace_id.'|'.$curDate.'|'.$curTime)
                ];


                // 如果开启了企业微信错误提示，发送信息，测试阶段，只给指定人发送
                Laminas::$serviceManager->swTaskClient->send([
                    'svName' => 'wechatWork',
                    'methodName' => 'sendMesToUser',
                    'params' => $msgData
                ]);

                $values = var_export($values, true);
            } elseif (is_object($values)) {
                $trace_id = uniqid('', true);
                if($values instanceof \Throwable) {
                    $msgData = [
                        'title' => "【{$taskData['domainName']}】异步任务{$taskData['svName']}->{$taskData['methodName']}() 发生致命错误",
                        'description' => "{$values->getMessage()}",
                        'url' => "{$taskData['REQUEST_SCHEME']}://{$taskData['domainName']}/common/errormsg?code=".rawurlencode($trace_id.'|'.$curDate.'|'.$curTime)
                    ];

                    Laminas::$serviceManager->wechatWork->sendMesToUser($msgData);

                    $values = $values->getTrace();
                    $values['errorMessage'] = $msgData['description'];
                    // print_r($values);
                    $values['trace_id'] = $trace_id;
                    $values['datetime'] = $curDate.' '.$curTime;

                    $values = var_export($values, true);
                }else{
                    $values = json_encode($values, JSON_FORCE_OBJECT);
                }
            }



            // 文件路径
            $filePath = APP_PATH . "/runtime/".date('Y-m-d')."/";
            // 路径不存在则创建
            !is_dir($filePath) && mkdir($filePath, 0777, true);

            // 写入文件
            return file_put_contents($filePath . str_ireplace(':', '_', $curTime).'_'.$trace_id . '.log', $values . PHP_EOL . PHP_EOL, FILE_APPEND|LOCK_EX);
        } catch (\Exception $e) {
            return false;
        }
    }

    /**
     * 获取用户登录信息
     * @return array|mixed|null
     */
    private function getIdentityData()
    {
        try {
            return Laminas::$serviceManager->identity->getIdentityData();
        } catch (\Throwable $e) {
            return [];
        }
    }
}
