<?php declare(strict_types=1);

namespace Amp\Parallel\Context\Internal;

use Amp\Parallel\Context\ContextPanicError;
use function Amp\Parallel\Context\formatFlattenedBacktrace;

/**
 * @psalm-import-type FlattenedTrace from ContextPanicError
 *
 * @internal
 */
trait ContextException
{
    /**
     * @param class-string<\Throwable> $className Original exception class name.
     * @param FlattenedTrace $originalTrace Backtrace generated by {@see flattenThrowableBacktrace()}.
     */
    public function __construct(
        private readonly string $className,
        private readonly string $originalMessage,
        private readonly int|string $originalCode,
        private readonly string $originalFile,
        private readonly int $originalLine,
        private readonly array $originalTrace,
        ?\Throwable $previous = null,
    ) {
        $format = '%s thrown in context with message "%s" and code "%s" in %s:%d'
            . '; call %s::getOriginalTrace() for the stack trace in the context as an array;'
            . ' if the Xdebug extension is enabled, set "xdebug.mode" to "debug" to include'
            . ' the exception stack trace in the context in the exception message';

        parent::__construct(\sprintf(
            $format,
            $className,
            $originalMessage,
            $originalCode,
            $originalFile,
            $originalLine,
            self::class,
        ), previous: $previous);
    }

    public function __toString(): string
    {
        return \sprintf("%s\nStack trace in context:\n%s", $this->getMessage(), $this->getOriginalTraceAsString());
    }

    /**
     * @return class-string<\Throwable> Original exception class name.
     */
    public function getOriginalClassName(): string
    {
        return $this->className;
    }

    /**
     * @return string Original exception message.
     */
    public function getOriginalMessage(): string
    {
        return $this->originalMessage;
    }

    /**
     * @return int|string Original exception code.
     */
    public function getOriginalCode(): int|string
    {
        return $this->originalCode;
    }

    public function getOriginalFile(): string
    {
        return $this->originalFile;
    }

    public function getOriginalLine(): int
    {
        return $this->originalLine;
    }

    /**
     * Returns the original exception stack trace.
     *
     * @return FlattenedTrace Same as {@see Throwable::getTrace()}, except all function arguments are formatted
     *      as strings. See {@see formatFlattenedBacktrace()}.
     */
    public function getOriginalTrace(): array
    {
        return $this->originalTrace;
    }

    /**
     * Original backtrace flattened to a human-readable string.
     */
    public function getOriginalTraceAsString(): string
    {
        return formatFlattenedBacktrace($this->originalTrace);
    }
}
