<?php

namespace App\Exceptions;

use App\Generated\Exceptions\ApiNotFoundException;
use App\Generated\Exceptions\AuthExpiredException;
use App\Generated\Exceptions\MaintainModeException;
use App\Generated\Exceptions\ServerInternalErrorException;
use App\Services\WeChat\Base;
use Exception;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\QueryException;
use Illuminate\Support\Facades\Config;
use Illuminate\Validation\ValidationException;
use Kamicloud\StubApi\Exceptions\BaseException;
use Sentry\SentryLaravel\SentryFacade;
use Symfony\Component\HttpKernel\Exception\HttpException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
use Symfony\Component\Routing\Exception\MethodNotAllowedException;
use Tymon\JWTAuth\Exceptions\JWTException;
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;

class Handler extends ExceptionHandler
{
    /**
     * A list of the exception types that are not reported.
     * 未报告的异常类型列表
     * @var array
     */
    protected $dontReport = [
        ApiException::class,
        AlertException::class,
        VersionException::class,
        JWTException::class,
        VersionException::class,
        DBException::class,
        NoticeException::class,
        ModelNotFoundException::class,
        WeChatException::class,
        BaseException::class,
    ];

    /**
     * A list of the inputs that are never flashed for validation exceptions.
     * 从不为验证异常刷新的输入列表
     * @var array
     */
    protected $dontFlash = [
        'password',
        'password_confirmation',
    ];

    /**
     * Report or log an exception.
     * 报告或记录异常
     *
     * This is a great spot to send exceptions to Sentry, Bugsnag, etc.
     * 这是一个向Sentry、Bugsnag,etc等发送例外情况的好地方。
     *
     * @param  \Exception  $exception
     * @return void
     * @throws Exception
     */
    public function report(Exception $exception)
    {
        if (app()->bound('sentry') && $this->shouldReport($exception)) {
            SentryFacade::captureException($exception, [
                'user' => [
                    'id' => Config::get('uid', null)
                ]
            ]);
        }
        parent::report($exception);
    }

    /**
     * Render an exception into an HTTP response.
     * 将异常呈现到HTTP响应中
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Exception  $exception
     * @return \Symfony\Component\HttpFoundation\Response
     * @throws Exception
     */
    public function render($request, Exception $exception)
    {
        if (starts_with($request->getRequestUri(), '/stub-api')) {
            if ($exception instanceof \Illuminate\Foundation\Http\Exceptions\MaintenanceModeException) {
                $exception = config('generator.exceptions.maintain-mode', MaintainModeException::class);
                $exception = new $exception('Maintaining');
            }
            if ($exception instanceof BaseException) {
                return response()->json($exception->toResponse($request));
            }
            if ($exception instanceof AlertException) {
                return parent::render($request, $exception);
            }
            if ($exception instanceof JWTException) {
                throw new AuthExpiredException('登陆已过期,请重新登陆');
            }
            if ($exception instanceof NotFoundHttpException) {
                throw new ApiNotFoundException('接口不存在');
            }
            if ($exception instanceof MethodNotAllowedException) {
                throw new ApiNotFoundException('请求方式不合法');
            }
            if (config('app.debug', false) !== true && !$request->input('__test_mode', false)) {
                return parent::render($request, $exception);
            } else {
                $exceptionClass = config(
                    'generator.exceptions.server-internal-error',
                    ServerInternalErrorException::class
                );
                return self::render($request, new $exceptionClass('Something went wrong.'));
            }
        } else {
            if ($exception instanceof JWTException) {
                return response([
                    'code' => 301,
                    'status_code' => 401,
                    'message' => "登陆已过期,请重新登陆",
                    'error_message' => $exception->getMessage()
                ]);
            }

            if ($exception instanceof ValidationException) {
                return response([
                    'code' => 422,
                    'status_code' => 422,
                    'message' => $exception->validator->errors()->first(),
                    'errors' => $exception->errors(),
                ]);
            }

            if ($exception instanceof HttpException) {
                return response([
                    'code' => $exception->getStatusCode(),
                    'status_code' => $exception->getStatusCode(),
                    'message' => '请求异常[http]',
                    'error_message' => $exception->getMessage() ?: "http请求错误"
                ]);
            }

            if ($exception instanceof QueryException) {
                return response([
                    'code' => 503,
                    'status_code' => 500,
                    'message' => "请求异常[DB]",
                    'error_message' => $exception->getMessage(),
                    'error_code' => $exception->getCode(),
                    'sql' => $exception->getSql(),
                ]);
            }

            if ($exception instanceof ModelNotFoundException) {
                return response([
                    'code' => 404,
                    'status_code' => 404,
                    'message' => '不存在',
                    'error_message' => $exception->getMessage(),
                    'model' => $exception->getModel()
                ]);
            }

            if ($request->get('debug') == 98047) {
                return response([
                    'code' => $exception->getCode(),
                    'message' => $exception->getMessage(),
                    'exception' => $exception->getPrevious(),
                    'file' => $exception->getFile(),
                    'line' => $exception->getLine(),
                    'trace' => $exception->getTrace()
                ]);
            } else {
                return parent::render($request, $exception);
            }
        }
    }
}
