<?php

namespace WP_Ultimo\Dependencies\Amp\Internal;

use WP_Ultimo\Dependencies\Amp\Coroutine;
use WP_Ultimo\Dependencies\Amp\Failure;
use WP_Ultimo\Dependencies\Amp\Loop;
use WP_Ultimo\Dependencies\Amp\Promise;
use WP_Ultimo\Dependencies\React\Promise\PromiseInterface as ReactPromise;
/**
 * Trait used by Promise implementations. Do not use this trait in your code, instead compose your class from one of
 * the available classes implementing \Amp\Promise.
 *
 * @internal
 */
trait Placeholder
{
    /** @var bool */
    private $resolved = \false;
    /** @var mixed */
    private $result;
    /** @var ResolutionQueue|null|callable(\Throwable|null, mixed): (Promise|\React\Promise\PromiseInterface|\Generator<mixed,
     *     Promise|\React\Promise\PromiseInterface|array<array-key, Promise|\React\Promise\PromiseInterface>, mixed,
     *     mixed>|null)|callable(\Throwable|null, mixed): void */
    private $onResolved;
    /** @var null|array */
    private $resolutionTrace;
    /**
     * @inheritdoc
     */
    public function onResolve(callable $onResolved)
    {
        if ($this->resolved) {
            if ($this->result instanceof Promise) {
                $this->result->onResolve($onResolved);
                return;
            }
            try {
                /** @var mixed $result */
                $result = $onResolved(null, $this->result);
                if ($result === null) {
                    return;
                }
                if ($result instanceof \Generator) {
                    $result = new Coroutine($result);
                }
                if ($result instanceof Promise || $result instanceof ReactPromise) {
                    Promise\rethrow($result);
                }
            } catch (\Throwable $exception) {
                Loop::defer(static function () use($exception) {
                    throw $exception;
                });
            }
            return;
        }
        if (null === $this->onResolved) {
            $this->onResolved = $onResolved;
            return;
        }
        if (!$this->onResolved instanceof ResolutionQueue) {
            /** @psalm-suppress InternalClass */
            $this->onResolved = new ResolutionQueue($this->onResolved);
        }
        /** @psalm-suppress InternalMethod */
        $this->onResolved->push($onResolved);
    }
    public function __destruct()
    {
        try {
            $this->result = null;
        } catch (\Throwable $e) {
            Loop::defer(static function () use($e) {
                throw $e;
            });
        }
    }
    /**
     * @param mixed $value
     *
     * @return void
     *
     * @throws \Error Thrown if the promise has already been resolved.
     */
    private function resolve($value = null)
    {
        if ($this->resolved) {
            $message = "Promise has already been resolved";
            if (isset($this->resolutionTrace)) {
                $trace = formatStacktrace($this->resolutionTrace);
                $message .= ". Previous resolution trace:\n\n{$trace}\n\n";
            } else {
                // @codeCoverageIgnoreStart
                $message .= ", define environment variable AMP_DEBUG or const AMP_DEBUG = true and enable assertions " . "for a stacktrace of the previous resolution.";
                // @codeCoverageIgnoreEnd
            }
            throw new \Error($message);
        }
        \assert((function () {
            $env = \getenv("AMP_DEBUG") ?: "0";
            if ($env !== "0" && $env !== "false" || \defined("AMP_DEBUG") && \AMP_DEBUG) {
                $trace = \debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS);
                \array_shift($trace);
                // remove current closure
                $this->resolutionTrace = $trace;
            }
            return \true;
        })());
        if ($value instanceof ReactPromise) {
            $value = Promise\adapt($value);
        }
        $this->resolved = \true;
        $this->result = $value;
        if ($this->onResolved === null) {
            return;
        }
        $onResolved = $this->onResolved;
        $this->onResolved = null;
        if ($this->result instanceof Promise) {
            $this->result->onResolve($onResolved);
            return;
        }
        try {
            /** @var mixed $result */
            $result = $onResolved(null, $this->result);
            $onResolved = null;
            // allow garbage collection of $onResolved, to catch any exceptions from destructors
            if ($result === null) {
                return;
            }
            if ($result instanceof \Generator) {
                $result = new Coroutine($result);
            }
            if ($result instanceof Promise || $result instanceof ReactPromise) {
                Promise\rethrow($result);
            }
        } catch (\Throwable $exception) {
            Loop::defer(static function () use($exception) {
                throw $exception;
            });
        }
    }
    /**
     * @param \Throwable $reason Failure reason.
     *
     * @return void
     */
    private function fail(\Throwable $reason)
    {
        $this->resolve(new Failure($reason));
    }
    /**
     * @return bool True if the placeholder has been resolved.
     */
    private function isResolved() : bool
    {
        return $this->resolved;
    }
}
