<?php namespace Common\Billing\Gateways\Stripe;

use Common\Billing\Models\Price;
use Common\Billing\Models\Product;
use Stripe\Exception\ApiErrorException;
use Stripe\Exception\InvalidRequestException;
use Stripe\Price as StripePrice;
use Stripe\StripeClient;

class StripePlans
{
    use FormatsMoney;

    public function __construct(protected StripeClient $client)
    {
    }

    public function sync(Product $product): bool
    {
        $product->load('prices');

        // create product on stripe, if it does not exist already
        try {
            $stripeProduct = $this->client->products->retrieve($product->uuid);
        } catch (ApiErrorException $err) {
            $stripeProduct = null;
        }

        if (!$stripeProduct) {
            $this->client->products->create([
                'id' => $product->uuid,
                'name' => $product->name,
            ]);
        }

        // create any local product prices on stripe, that don't exist there already
        $product->prices->each(function (Price $price) use ($product) {
            if (
                !$price->stripe_id ||
                !$this->priceExistsOnStripe($price->stripe_id)
            ) {
                $this->createPrice($product, $price);
            }
        });

        return true;
    }

    public function createPrice(Product $product, Price $price): StripePrice
    {
        $stripePrice = $this->client->prices->create([
            'product' => $product->uuid,
            'unit_amount' => $this->priceToCents($price),
            'currency' => $price->currency,
            'recurring' => [
                'interval' => $price->interval,
                'interval_count' => $price->interval_count,
            ],
        ]);

        $price->fill(['stripe_id' => $stripePrice->id])->save();

        return $stripePrice;
    }

    public function delete(Product $product): bool
    {
        // stripe does not allow deleting product if it has prices attached,
        // and prices can't be deleted via API, we archive the product instead
        try {
            $this->client->products->update($product->uuid, [
                'active' => false,
            ]);
        } catch (InvalidRequestException $e) {
            // if this product is already deleted on stripe, ignore
            if ($e->getStripeCode() !== 'resource_missing') {
                throw $e;
            }
        }
        return true;
    }

    public function getAll(): array
    {
        return $this->client->products->all()->toArray();
    }

    protected function priceExistsOnStripe(string $stripePriceId): bool
    {
        try {
            $this->client->prices->retrieve($stripePriceId);
            return true;
        } catch (InvalidRequestException $e) {
            return false;
        }
    }
}
