<?php

namespace YooMoney\Helpers;

use Exception;
use Psr\Log\LoggerInterface;
use Psr\Log\LogLevel;
use RuntimeException;

/**
 * Класс методов для работы с логами модуля
 */
class LoggerHelper implements LoggerInterface
{
    const OAUTH_CMS_URL = 'https://yookassa.ru/integration/oauth-cms';

    private $shopId = null;

    public function __construct($shopId = null)
    {
        $this->shopId = $shopId;
    }

    /**
     * Выполняет подготовку данных и запись лога в файл
     *
     * @param $level
     * @param $message
     * @param array $context
     */
    public function log($level, $message, array $context = array())
    {
        $replace = $this->prepareContext($context);

        $message = strip_tags($message);

        if (!empty($replace)) {
            $message = $this->prepareMessage($replace, $message);
        }

        $this->write($this->getLogFileName(), ' [' . $level . '] ' . $message);
    }

    /**
     * Возвращает путь к файлу лога
     *
     * @return string
     */
    public function getLogFileName()
    {
        return realpath(JSH_DIR).'/log/pm_yoomoney.log';
    }

    /**
     * Подготовка данных из контекста сообщения
     *
     * @param array $context
     * @return array|void
     */
    private function prepareContext($context)
    {
        if (!$context) {
            return;
        }

        $replace = array();
        foreach ($context as $key => $value) {
            $replace['{'.$key.'}'] = (is_array($value) || is_object($value)) ? json_encode($value, JSON_PRETTY_PRINT) : $value;
        }

        return $replace;
    }

    /**
     * Разбивка данных и формирование сообщения
     *
     * @param array $replace
     * @param string $message
     * @return string
     */
    private function prepareMessage($replace, $message)
    {
        foreach ($replace as $key => $object) {
            $label = trim($key, "{}");
            $message .= " \n{$label}: {$object}";
        }

        return $message;
    }

    /**
     * Открытие файла для записи, запись и закрытие файла
     *
     * @param string $fileName
     * @param string $message
     * @return void
     */
    private function write($fileName, $message)
    {
        $fd = @fopen($fileName, 'a');

        if ($fd) {
            flock($fd, LOCK_EX);
            fwrite($fd, date(DATE_ATOM). $message ."\r\n");
            flock($fd, LOCK_UN);
            fclose($fd);
        }
    }

    public function emergency($message, array $context = array())
    {
        $this->log(LogLevel::EMERGENCY, $message, $context);
    }

    public function alert($message, array $context = array())
    {
        $this->log(LogLevel::ALERT, $message, $context);
    }

    public function critical($message, array $context = array())
    {
        $this->log(LogLevel::CRITICAL, $message, $context);
    }

    public function error($message, array $context = array())
    {
        $this->log(LogLevel::ERROR, $message, $context);
    }

    public function warning($message, array $context = array())
    {
        $this->log(LogLevel::WARNING, $message, $context);
    }

    public function notice($message, array $context = array())
    {
        $this->log(LogLevel::NOTICE, $message, $context);
    }

    public function info($message, array $context = array())
    {
        $this->log(LogLevel::INFO, $message, $context);
    }

    public function debug($message, array $context = array())
    {
        $this->log(LogLevel::DEBUG, $message, $context);
    }

    /**
     * @param $data
     * @return bool
     */
    public function sendMetric($data)
    {
        $parameters = array(
            'cms' => 'joomshopping3-4',
            'host' => $_SERVER['HTTP_HOST'],
            'shop_id' => $this->shopId ?: null,
        );

        try {
            $options = array(
                CURLOPT_URL => self::OAUTH_CMS_URL . '/metric/joomshopping',
                CURLOPT_POSTFIELDS => json_encode(array_merge($data, $parameters), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
            );
            $this->makeRequest($options);
        } catch (Exception $e) {
            $this->log('error', 'Failed to send metric: ' . $e->getMessage());
            return false;
        }

        return true;
    }

    /**
     * @param array $metrics
     * @return void
     */
    public function sendHeka($metrics)
    {
        $this->sendMetric(array(
            'metric_heka' => $metrics
        ));
    }

    /**
     * @param string $type
     * @param array $metrics
     * @return void
     */
    public function sendBI($type, $metrics)
    {
        $this->sendMetric(array(
            'metric_bi' => array(
                'type' => $type,
                'data' => $metrics
            )
        ));
    }

    /**
     * @param string $message
     * @param array $context
     * @param array $metrics
     * @return void
     */
    public function sendAlertLog($message, $context = array(), $metrics = array())
    {
        if (!empty($context['exception']) && $context['exception'] instanceof Exception) {
            $exception = $context['exception'];
            $context['exception'] = array(
                'code' => $exception->getCode(),
                'message' => $exception->getMessage(),
                'file' => $exception->getFile() . ':' . $exception->getLine(),
                'trace' => $exception->getTraceAsString(),
            );
        }
        $data = array(
            'metric_app' => array(
                'level' => 'alert',
                'message' => $message,
                'context' => $context,
            )
        );
        if (!empty($metrics)) {
            $data['metric_heka'] = $metrics;
        }
        $this->sendMetric($data);
    }

    /**
     * Выполняет запрос с полученными параметрами
     *
     * @param array $options - массив curl опций
     * @return void
     * @throws Exception
     */
    private function makeRequest($options)
    {
        $optionsConst = array(
            CURLOPT_HTTPHEADER => array('Content-Type:application/json;charset=utf-8'),
            CURLOPT_POST => 1,
            CURLOPT_RETURNTRANSFER => 1
        );
        $options = $optionsConst + $options;
        $ch = curl_init();
        curl_setopt_array($ch, $options);
        $result = curl_exec($ch);
        $status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($status !== 200) {
            throw new RuntimeException(
                'Response status code is not 200. Code: ' . $status . ' Response: ' . $result
            );
        }
    }

}
