<?php
declare(strict_types=1);

namespace App\Utils\Transfer;

use Exception;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Support\Facades\Config;
use GuzzleHttp\Client;
use Psr\Http\Message\ResponseInterface;

abstract class HttpTransferAbstract
{
    // HTTP 客户端
    private Client $client;

    // His接口配置数据
    private array $his_config;

    // 调用方法
    public string $transfer_method;

    // 调用接口名称
    public string $transfer_name;

    // 调用接口参数
    public mixed $transfer_parameter;

    // 调用返回结果 string格式用于mock数据
    public ResponseInterface|string $transfer_response;

    // 运行时间
    public array $request_time;

    /**
     * HttpTransferAbstract constructor.
     * @param string $his_name
     */
    public function __construct(string $his_name)
    {
        $config = Config::get('hisservice.'. $his_name);

        // 获取配置文件中的接口配置信息
        $this->his_config = $config;
        $this->his_config['his_name'] = $his_name;

        $this->initialize();
    }

    /**
     * 初始化
     */
    public function initialize(): void
    {
        $headers = $this->clientHeaders();
        $this->client = new Client([
            'base_uri' => $this->his_config['url'],
            'headers' => $headers,
            ... $this->his_config['options']
        ]);
    }

    /**
     * 获取配置
     * @param string $key
     * @return mixed|null
     */
    public function getHisConfigByKey(string $key): mixed
    {
        if (isset($this->his_config[$key])) {
            return $this->his_config[$key];
        }

        return false;
    }

    /**
     * 设置客户端 Header 头
     * @return array
     */
    abstract public function clientHeaders(): array;

    /**
     * 需要调用的方法
     * @param string $method
     * @param string $request_name
     * @param array $request_data
     * @return $this
     * @throws Exception
     */
    public function transferMethod(string $method, string $request_name, array $request_data = []): self
    {
        // 记录调用的接口名称和参数
        $this->transfer_method = $method;
        $this->transfer_name = $request_name;
        $this->transfer_parameter = $request_data;

        try {
            // 发送 HTTP 请求
            $this->request_time['start_time'] = microtime(true);
            $this->transfer_response = $this->client->request($method, $this->transfer_name, $this->transfer_parameter);
            $this->request_time['end_time'] = microtime(true);
        } catch (GuzzleException|Exception $e) {
            !isset($this->request_time['end_time']) && $this->request_time['end_time'] = microtime(true);
            $this->recordLog();
            throw new Exception("{$e->getFile()}:{$e->getLine()}:{$e->getMessage()}");
        }

        // 记录日志
        $this->recordLog();
        return $this;
    }

    /**
     * 获取返回值
     * @param bool $is_format
     * @return mixed
     * @throws Exception
     */
    public function getResult(bool $is_format = true): mixed
    {
        if ($is_format) {
            return $this->responseFormat($this->transfer_response);
        }

        return $this->transfer_response;
    }

    /**
     * 响应格式化
     * @param mixed $data
     * @return mixed
     */
    abstract public function responseFormat(mixed $data): mixed;

    /**
     * 魔术方法实现动态调用方法
     * @param $function
     * @param $args
     * @return $this
     * @throws Exception
     */
    public function __call($function, $args): self
    {
        if (method_exists($this, $function)) {
            throw new Exception(__CLASS__ . '类的"'. $function .'"方法不存在');
        }

        $this->client = call_user_func($function, ...$args);
        return $this;
    }

    /**
     * 记录日志
     */
    public function recordLog(): void
    {
        // 判断“是否设置日志参数”和“当前调用的方法是否记录到日志”
        if (!empty($this->transfer_name)) {
            $run_time = sprintf("%.6f", ($this->request_time['end_time'] - $this->request_time['start_time']));

            if  (
                empty($this->his_config['not_log_arr']) ||
                !in_array($this->transfer_name, $this->his_config['not_log_arr'])
            ) {
                // 记录入参和结果
                $content = '[METHOD NAME] '. $this->transfer_method. '|'. $this->transfer_name. PHP_EOL.
                    '[REQUEST PARAM] '. json_encode($this->transfer_parameter, JSON_UNESCAPED_UNICODE). PHP_EOL.
                    '[RESPONSE PARAM] '. json_encode($this->transfer_response, JSON_UNESCAPED_UNICODE). PHP_EOL.
                    '[RUN TIME] '. $run_time . "/s";
                $this->recordRequestLog($this->his_config['his_name'], $content);
            }
        }
    }

    /**
     * 保存日志记录
     * @param string $his_name
     * @param string $content
     */
    protected function recordRequestLog(string $his_name, string $content): void
    {
        date_default_timezone_set("Asia/Shanghai");

        $dirname = $this->his_config['his_name'];
        $path = app()->storagePath(). DIRECTORY_SEPARATOR. $dirname. DIRECTORY_SEPARATOR;
        $file_path = $path. $his_name. 'Log'. DIRECTORY_SEPARATOR. date('Ym'). DIRECTORY_SEPARATOR;
        $file_name = date('d'). ".log";

        !is_dir($file_path) && mkdir($file_path, 0755, true);

        $msg = "[".date('Y-m-d H:i:s')."]". PHP_EOL . $content . PHP_EOL . PHP_EOL;

        file_put_contents( $file_path. $file_name, $msg, FILE_APPEND);
    }
}