<?php

declare(strict_types=1);

namespace App\Services;

use App\Enums\Status;
use App\Models\Charge;
use App\Models\Invoice as InvoiceModel;
use App\Models\Order;
use App\Models\Payment;
use App\Models\User;
use App\Models\WalletTransaction;
use Illuminate\Http\Request;
use Shetabit\Multipay\Exceptions\InvalidPaymentException;
use Shetabit\Multipay\Invoice;
use Shetabit\Multipay\RedirectionForm;
use Shetabit\Payment\Facade\Payment as Pay;

class PaymentService
{
    /**
     * Start a gateway purchase and return payment + redirect form data.
     *
     * @return array{payment: Payment, gateway_url: string, gateway: array{action: string, method: string, inputs: array<string, mixed>}}
     */
    public function startGateway(
        User $user,
        int $price,
        string $modelType,
        int $modelId,
        string $via = 'zibal',
        ?string $orderId = null,
        ?int $projectId = null,
    ): array {
        $payment = Payment::create([
            'user_id' => $user->id,
            'created_by' => $user->id,
            'price' => $price,
            'model_type' => $modelType,
            'model_id' => $modelId,
            'project_id' => $projectId,
            'gateway' => $via,
            'status' => Status::PROCESSING->value,
        ]);

        $callbackUrl = rtrim((string) config('app.url'), '/')
            . '/api/pay/verify/' . $payment->id
            . '?token=' . urlencode((string) $payment->token)
            . '&gateway=' . urlencode($via)
            . '&type=' . urlencode(str_replace('App\\Models\\', '', $modelType));

        $invoice = (new Invoice)->amount($price);

        $description = match (true) {
            $modelType === InvoiceModel::class => 'پرداخت فاکتور ' . ($orderId ?: $modelId),
            $modelType === Order::class => 'پرداخت سفارش ' . ($orderId ?: $modelId),
            $modelType === Charge::class => 'شارژ کیف پول #' . ($orderId ?: $modelId),
            default => 'پرداخت #' . ($orderId ?: $modelId),
        };

        $invoice->detail([
            'description' => $description,
            'orderId' => $orderId ?: (string) $modelId,
        ]);

        /** @var RedirectionForm $redirection */
        $redirection = Pay::via($via)
            ->callbackUrl($callbackUrl)
            ->purchase(
                $invoice,
                function ($driver, $transactionId) use ($payment): void {
                    $payment->update([
                        'transaction_id' => (string) $transactionId,
                    ]);
                }
            )
            ->pay();

        $gatewayUrl = $redirection->getAction();

        $payment->update([
            'ref_url' => $gatewayUrl,
        ]);

        return [
            'payment' => $payment->fresh(),
            'gateway_url' => $gatewayUrl,
            'gateway' => [
                'action' => $gatewayUrl,
                'method' => $redirection->getMethod(),
                'inputs' => $redirection->getInputs(),
            ],
        ];
    }

    public function pay(User $user, $price, $model_type, $model_id, $via, $ref_url = null)
    {
        if ($via === 'wallet') {
            return Payment::create([
                'user_id' => $user->id,
                'created_by' => $user->id,
                'price' => $price,
                'model_type' => $model_type,
                'model_id' => $model_id,
                'gateway' => $via,
                'ref_url' => $ref_url,
                'status' => Status::PROCESSING->value,
            ]);
        }

        $result = $this->startGateway(
            $user,
            (int) $price,
            $model_type,
            (int) $model_id,
            $via,
        );

        return $result['gateway'];
    }

    public function verify(Request $request, Payment $payment): Payment
    {
        if ((int) $payment->status === Status::PAID->value) {
            return $payment;
        }

        try {
            $trackId = $request->input('trackId')
                ?? $request->input('Authority')
                ?? $payment->transaction_id;

            $receipt = Pay::amount((int) $payment->price)
                ->via($payment->gateway)
                ->transactionId($trackId)
                ->verify();

            $payment->update([
                'ref_id' => $receipt->getReferenceId(),
                'status' => Status::PAID->value,
                'transaction_id' => $trackId ?: $payment->transaction_id,
            ]);

            if ($payment->model_type === Order::class && $payment->model_id) {
                $order = Order::find($payment->model_id);
                if ($order) {
                    (new OrderService)->markGroupPaid($order);
                }
            }

            if ($payment->model_type === InvoiceModel::class && $payment->model_id) {
                $this->markInvoicePaid($payment);
            }

            if ($payment->model_type === Charge::class && $payment->model_id) {
                $this->markChargePaid($payment);
            }

            return $payment->fresh();
        } catch (InvalidPaymentException $exception) {
            throw $exception;
        }
    }

    private function markInvoicePaid(Payment $payment): void
    {
        $invoice = InvoiceModel::find($payment->model_id);
        if (! $invoice || $invoice->status === InvoiceModel::STATUS_PAID) {
            return;
        }

        $invoice->update(['status' => InvoiceModel::STATUS_PAID]);

        $user = User::find($payment->user_id);

        WalletTransaction::create([
            'user_id' => $payment->user_id,
            'type' => WalletTransaction::TYPE_PURCHASE,
            'amount' => (int) $payment->price,
            'balance_after' => (int) ($user?->wallet ?? 0),
            'description' => 'پرداخت فاکتور ' . ($invoice->number ?? $invoice->id) . ' از درگاه',
            'tracking_code' => $payment->ref_id,
            'status' => WalletTransaction::STATUS_COMPLETED,
            'project_id' => $invoice->project_id ?? $payment->project_id,
            'invoice_id' => $invoice->id,
            'payment_id' => $payment->id,
        ]);
    }

    private function markChargePaid(Payment $payment): void
    {
        $charge = Charge::find($payment->model_id);
        if (! $charge || $charge->status === 'paid') {
            return;
        }

        $charge->update(['status' => 'paid']);

        $user = User::find($payment->user_id);
        if (! $user) {
            return;
        }

        app(WalletService::class)->credit(
            $user,
            (int) $payment->price,
            WalletTransaction::TYPE_DEPOSIT,
            'شارژ کیف پول از درگاه',
            ['charge_id' => $charge->id],
            paymentId: $payment->id,
            trackingCode: $payment->ref_id,
        );
    }

    public function postVerify(Request $request, Payment $payment)
    {
        $receipt = Pay::amount((int) $payment->price)->via('sepehr')->transactionId($request->digitalreceipt)->verify();
        $payment->update([
            'ref_id' => $receipt->getReferenceId(),
        ]);
    }
}
