<?php

namespace App\Modules\OnlinePayment\Repositories;

use App\Modules\AppHelpers\Contracts\HasOnlinePaymentStatusFields;
use App\Modules\Makeable;
use App\Modules\OnlinePayment\Contracts\HasOnlinePayments;
use App\Modules\OnlinePayment\Contracts\PaymentProviderContract;
use App\Modules\OnlinePayment\Models\OnlinePayment;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Client\Response;

class OnlinePaymentRepository implements HasOnlinePaymentStatusFields
{
    use Makeable;

    /**
     * Pending online payments
     */
    public const PENDING = 'pending';

    /**
     * Failed online payments
     */
    public const FAILED = 'failed';

    /**
     * Paid online payments
     */
    public const PAID = 'paid';

    /**
     * Related model
     */
    protected ?Model $relatedModel;

    /**
     * Payment provider
     */
    protected PaymentProviderContract $provider;

    /**
     * Response
     */
    protected Response $response;

    /**
     * If payment is successful
     */
    protected bool $successful;

    /**
     * If payment has failed
     */
    protected bool $failed;

    /**
     * Payment order id
     */
    protected string $orderId;

    /**
     * Online payment link
     */
    protected string $paymentLink;

    public function __construct(?Model $relatedModel)
    {
        $this->relatedModel = $relatedModel;
    }

    /**
     * Status Values
     *
     * @return array<null|string, string>
     */
    public static function statusValues(): array
    {
        return [
            null => '-',
            self::PENDING => __('Pending'),
            self::PAID => __('Paid'),
            self::FAILED => __('Cancelled'),
        ];
    }

    /**
     * Generate order number for payment
     */
    public function generateOrderNumber(): string
    {
        return date('dmyHis');
    }

    /**
     * Payment provder
     */
    public function paymentProvider(PaymentProviderContract $provider): self
    {
        $this->provider = $provider;

        return $this;
    }

    /**
     * If payment has been successfull
     */
    public function successful(): bool
    {
        return $this->successful;
    }

    /**
     * If payment has been a failure
     */
    public function failed(): bool
    {
        return $this->failed;
    }

    /**
     * Set respond results
     */
    public function setResponseResults(bool $result): void
    {
        $this->successful = $result;
        $this->failed = ! $result;
    }

    /**
     * Send request via provider
     */
    public function sendRequest(): self
    {
        $this->response = $this->provider->sendRequest();

        $this->failed = $this->response->failed();
        $this->successful = $this->response->successful();

        if ($this->response['errorCode'] != 0) {
            $this->setResponseResults(false);
        }

        if ($this->successful) {
            $this->orderId = string($this->response['orderId']);
            $this->paymentLink = string($this->response['formUrl']);

            $this->createHistory();
        }

        return $this;
    }

    /**
     * Payment link
     */
    public function paymentLink(): string
    {
        return $this->paymentLink;
    }

    /**
     * Create online payment history
     */
    public function createHistory(): void
    {
        $data = [
            'amount' => $this->provider->amount(),
            'orderNumber' => $this->provider->orderNumber(),
            'description' => $this->provider->description(),
            'orderId' => $this->orderId,
            'formUrl' => $this->paymentLink,
            'successUrl' => $this->provider->returnUrl(),
            'errorUrl' => $this->provider->returnUrl(),
            'username' => $this->provider->username(),
            'paymentStatus' => self::PENDING,
        ];

        if ($this->relatedModel) {
            $data['online_paymantable_id'] = $this->relatedModel->id; // @phpstan-ignore-line
            $data['online_paymantable_type'] = $this->relatedModel::class;
        }

        OnlinePayment::create($data);
    }
}
