<?php

declare(strict_types=1);

namespace WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Container;

use WooCommerce\PayPalCommerce\Vendor\Inpsyde\Modularity\Package;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerExceptionInterface;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;

class PackageProxyContainer implements ContainerInterface
{
    /**
     * @var Package
     */
    private $package;

    /**
     * @var ContainerInterface|null
     */
    private $container;

    /**
     * @param Package $package
     */
    public function __construct(Package $package)
    {
        $this->package = $package;
    }

    /**
     * @param string $id
     * @return mixed
     *
     * @throws \Exception
     */
    public function get(string $id)
    {
        $this->assertPackageBooted($id);

        return $this->container->get($id);
    }

    /**
     * @param string $id
     * @return bool
     *
     * @throws \Exception
     */
    public function has(string $id): bool
    {
        return $this->tryContainer() && $this->container->has($id);
    }

    /**
     * @return bool
     *
     * @throws \Exception
     * @psalm-assert-if-true ContainerInterface $this->container
     */
    private function tryContainer(): bool
    {
        if ($this->container) {
            return true;
        }

        /** TODO: We need a better way to deal with status checking besides equality */
        if (
            $this->package->statusIs(Package::STATUS_READY)
            || $this->package->statusIs(Package::STATUS_BOOTED)
        ) {
            $this->container = $this->package->container();
        }

        return (bool)$this->container;
    }

    /**
     * @param string $id
     * @return void
     *
     * @throws \Exception
     *
     * @psalm-assert ContainerInterface $this->container
     */
    private function assertPackageBooted(string $id): void
    {
        if ($this->tryContainer()) {
            return;
        }

        $name = $this->package->name();
        $status = $this->package->statusIs(Package::STATUS_FAILED)
            ? 'is errored'
            : 'is not ready yet';

        throw new class ("Error retrieving service {$id} because package {$name} {$status}.")
            extends \Exception
            implements ContainerExceptionInterface {
        };
    }
}
