<?php

namespace Dipper\Foundation\Exceptions;

use Log;
use Exception as BaseException;
use Illuminate\Support\Facades\Config;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\HttpException as SymfonyHttpException;

/**
 * Class Exception.
 */
abstract class ExceptionAbstract extends SymfonyHttpException
{
    /**
     * Default status code.
     *
     * @var int
     */
    const DEFAULT_STATUS_CODE = Response::HTTP_INTERNAL_SERVER_ERROR;

    /**
     * Default error code.
     *
     * @var int
     */
    const DEFAULT_CODE = -1;

    /**
     * @var string
     */
    protected $debug;

    /**
     * HTTP状态码
     *
     * @var integer
     */
    public $httpStatusCode = Response::HTTP_BAD_REQUEST;
    
    /**
     * 错误消息
     *
     * @var string
     */
    public $message = '请求失败, 请稍后重试';

    /**
     * 错误码
     *
     * @var integer
     */
    public $code = -1;

    /**
     * 返回数据
     *
     * @var mixed
     */
    protected $data;

    /**
     * Exception constructor.
     *
     * @param null            $message
     * @param null            $statusCode
     * @param mixed           $data
     * @param \Exception|null $previous
     * @param array           $headers
     * @param int             $options
     */
    public function __construct($message = null, $code = null, $data = null, $statusCode = null, $headers = [], $options = 0)
    {
        // detect and set the running environment
        $this->debug = config('app.debug');

        $message = $this->prepareMessage($message);
        $code = $this->prepareCode($code);
        $statusCode = $this->prepareStatusCode($statusCode);

        $this->logTheError($message, $code, $data, $statusCode);

        parent::__construct($statusCode, $message, null, $headers, $code);
    }

    /**
     * 获取 $data.
     *
     * @return $data
     */
    public function getData()
    {
        return $this->data;
    }

    /**
     * Help developers debug the error without showing these details to the end user.
     * Usage: `throw (new MyCustomException())->debug($e)`.
     *
     * @param $error
     * @param $force
     *
     * @return $this
     */
    public function debug($error, $force = false)
    {
        if ($error instanceof BaseException) {
            $error = $error->getMessage();
        }

        if ($this->debug || $force === true) {
            Log::error('DEBUG', ['error' => $error]);
        }

        return $this;
    }

    /**
     * 日志
     *
     * @param string $message
     * @param int $code
     * @param mixed $data
     * @param int $statusCode
     * @return void
     */
    private function logTheError($message, $code, $data, $statusCode)
    {
        if ($this->debug) {
            Log::error('ERROR', [
                'Code' => $code,
                'StatusCode' => $statusCode,
                'Message' => $message,
                'Data' => $data,
            ]);
        }
    }

    /**
     * @param null $message
     *
     * @return  null
     */
    private function prepareMessage($message = null)
    {
        return is_null($message) && property_exists($this, 'message') ? $this->message : $message;
    }

    /**
     * @param $code
     *
     * @return  int
     */
    private function prepareCode($code = null)
    {
        return is_null($code) ? $this->findCode() : $code;
    }

    /**
     * @return  int
     */
    private function findCode()
    {
        return property_exists($this, 'code') ? $this->code : self::DEFAULT_CODE;
    }

    /**
     * @param $statusCode
     *
     * @return  int
     */
    private function prepareStatusCode($statusCode = null)
    {
        return is_null($statusCode) ? $this->findStatusCode() : $statusCode;
    }

    /**
     * @return  int
     */
    private function findStatusCode()
    {
        return property_exists($this, 'httpStatusCode') ? $this->httpStatusCode : self::DEFAULT_STATUS_CODE;
    }
}
