<?php

namespace App\Modules\OnlinePayment\Repositories;

use App\Modules\Makeable;
use App\Modules\OnlinePayment\Contracts\PaymentProviderContract;
use App\Modules\OnlinePayment\Models\OnlinePayment;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\Client\Response;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Log;

class OnlinePaymentRepository
{
    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;

    /**
     * Online payment
     */
    protected OnlinePayment $onlinePayment;

    /**
     * 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 = null)
    {
        $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 setPaymentProvider(PaymentProviderContract $provider): self
    {
        $this->provider = $provider;

        return $this;
    }

    /**
     * Payment provider
     */
    public function paymentProvider(): PaymentProviderContract
    {
        return $this->provider;
    }

    /**
     * Online payment
     */
    public function onlinePayment(): OnlinePayment
    {
        return $this->onlinePayment;
    }

    /**
     * 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();
        } else {
            Config::set('logging.channels.halkbank_payment_error', [
                'driver' => 'single',
                'path' => storage_path('logs/halkbank_payment_error.log'),
                'level' => 'debug',
            ]);

            Log::channel('halkbank_payment_error')
                ->error('Payment error', [
                    'response' => [
                        'title' => 'REGISTER',
                        'error' => $this->response->body(),
                        'file_line' => __FILE__.':'.__LINE__,
                    ],
                ]);
        }

        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;
        }

        $this->onlinePayment = OnlinePayment::create($data);
    }

    /**
     * Check payment
     *
     * @return array<string, string|bool|int>
     */
    public function checkPayment(string $orderId): array
    {
        // Find payment order from history
        /** @var \App\Modules\OnlinePayment\Models\OnlinePayment */
        $paymentHistory = OnlinePayment::where('orderId', $orderId)->first();

        // Resolve related resource model dynamically
        /** @var \Illuminate\Database\Eloquent\Model Find related resource */
        $modelClass = $paymentHistory->online_paymantable_type;
        $modelId = $paymentHistory->online_paymantable_id;

        if (! class_exists($modelClass)) {
            return $this->paymentFailed('(RELATED RESOURCE CLASS NOT FOUND)');
        }

        $relatedResource = (new $modelClass)->find(id: $modelId);

        // Check if resource could not be found or does not exist
        if (! $relatedResource) {
            return $this->paymentFailed('(RESOURCE NOT FOUND)');
        }

        $relatedResource->loadMissing('branch');

        /** @var \App\Modules\Branch\Models\Branch */
        $bankBranch = $relatedResource->branch;

        if (! $bankBranch || is_null($bankBranch->billing_username) || is_null($bankBranch->billing_password)) { // @phpstan-ignore-line
            return $this->paymentFailed('(BRANCH NOT FOUND)');
        }

        $this->provider->setUsername($bankBranch->billing_username);
        $this->provider->setPassword($bankBranch->billing_password);
        $response = $this->provider->checkPayment($orderId);

        if ($response['errorCode'] == '99') {
            return $this->paymentFailed('(REQUEST FAILURE)');
        }

        return $response['paymentAmountInfo']['depositedAmount'] > 0 // @phpstan-ignore-line
            ? $this->paymentSuccessful($relatedResource, $paymentHistory, $bankBranch->name)
            : $this->paymentFailed(__('Payment has failed'), $paymentHistory, $bankBranch->name);
    }

    /**
     * Successful payment
     *
     * @return array<string, string|bool|int>
     */
    public function paymentSuccessful(Model $relatedResource, OnlinePayment $paymentHistory, string $branchName = ''): array
    {
        $relatedResource->update([
            'paid' => true,
        ]);

        $paymentHistory->update([
            'paymentStatus' => OnlinePaymentRepository::PAID,
        ]);

        return $this->paymentCheckResponseTemplate(
            success: true,
            pnr: $paymentHistory->online_paymantable_id,
            branch_name: $branchName,
            price_amount: $paymentHistory->amount.' TMT'
        );
    }

    /**
     * Failed payment
     *
     * @return array<string, string|bool|int>
     */
    public function paymentFailed(string $title = '', ?OnlinePayment $paymentHistory = null, string $branchName = ''): array
    {
        if ($paymentHistory) {
            $paymentHistory->update([
                'paymentStatus' => OnlinePaymentRepository::FAILED,
            ]);

            return $this->paymentCheckResponseTemplate(
                success: false,
                pnr: $paymentHistory->online_paymantable_id,
                branch_name: $branchName,
                price_amount: $paymentHistory->amount.' TMT'
            );
        }

        return $this->paymentCheckResponseTemplate(false, $title);
    }

    /**
     * Check payment response
     *
     * @return array<string, string|bool|int>
     */
    public function paymentCheckResponseTemplate(
        bool $success,
        string $title = '',
        int|string $pnr = '',
        string $branch_name = '',
        int|string $price_amount = '',
        string $return_url = ''
    ): array {
        return [
            'success' => $success,
            'title' => $title !== '' ? $title : ($success ? __('Payment is successful') : __('Payment has failed')),
            'pnr' => $pnr,
            'branch_name' => $branch_name,
            'price_amount' => $price_amount,
            'return_url' => $return_url !== '' ? $return_url : url('/'),
        ];
    }

    /**
     * Show payment status
     *
     * @param  array<string, string|bool|int>  $data
     */
    public function paymentStatusView(array $data): View
    {
        return view('module.online-payment::payment-status', $data);
    }
}
