<?php

namespace Sentry\Laravel\Logs;

use Sentry\Logs\LogLevel;
use Monolog\Formatter\LineFormatter;
use Monolog\Formatter\FormatterInterface;
use Monolog\Handler\AbstractProcessingHandler;
use Sentry\Monolog\CompatibilityProcessingHandlerTrait;
use Sentry\Severity;
use Throwable;

use function Sentry\logger;

class LogsHandler extends AbstractProcessingHandler
{
    use CompatibilityProcessingHandlerTrait;

    /**
     * @var FormatterInterface The formatter to use for the logs generated via handleBatch()
     */
    protected $batchFormatter;

    /**
     * {@inheritdoc}
     */
    public function handleBatch(array $records): void
    {
        $level = $this->level;

        // filter records based on their level
        $records = array_filter(
            $records,
            function ($record) use ($level) {
                return $record['level'] >= $level;
            }
        );

        if (!$records) {
            return;
        }

        // the record with the highest severity is the "main" one
        $record = array_reduce(
            $records,
            function ($highest, $record) {
                if ($highest === null || $record['level'] > $highest['level']) {
                    return $record;
                }

                return $highest;
            }
        );

        // the other ones are added as a context item
        $logs = [];
        foreach ($records as $r) {
            $logs[] = $this->processRecord($r);
        }

        if ($logs) {
            $record['context']['logs'] = (string)$this->getBatchFormatter()->formatBatch($logs);
        }

        $this->handle($record);
    }

    /**
     * Sets the formatter for the logs generated by handleBatch().
     *
     * @param FormatterInterface $formatter
     *
     * @return \Sentry\Laravel\SentryHandler
     */
    public function setBatchFormatter(FormatterInterface $formatter): self
    {
        $this->batchFormatter = $formatter;

        return $this;
    }

    /**
     * Gets the formatter for the logs generated by handleBatch().
     */
    public function getBatchFormatter(): FormatterInterface
    {
        if (!$this->batchFormatter) {
            $this->batchFormatter = new LineFormatter();
        }

        return $this->batchFormatter;
    }

    /**
     * {@inheritdoc}
     * @suppress PhanTypeMismatchArgument
     */
    protected function doWrite($record): void
    {
        $context = $record['context'];
        $exception = $context['exception'] ?? null;

        if ($exception instanceof Throwable) {
            // Unset the exception object from the log context
            unset($context['exception']);
        }

        logger()->aggregator()->add(
            // This seems a little bit of a roundabout way to get the log level, but this is done for compatibility
            self::getLogLevelFromSeverity(
                self::getSeverityFromLevel($record['level'])
            ),
            $record['message'],
            [],
            array_merge($context, $record['extra'], ['sentry.origin' => 'auto.logger.monolog'])
        );
    }

    private static function getLogLevelFromSeverity(Severity $severity): LogLevel
    {
        switch ($severity) {
            case Severity::debug():
                return LogLevel::debug();
            case Severity::warning():
                return LogLevel::warn();
            case Severity::error():
                return LogLevel::error();
            case Severity::fatal():
                return LogLevel::fatal();
            default:
                return LogLevel::info();
        }
    }
}
