<?php

namespace App\Services\UPG;

use App\Helpers\UUIDHelper;
use App\Models\Setting;
use App\Models\Transaction;
use Carbon\Carbon;
use Illuminate\Support\Facades\Config;
use Shetabit\Multipay\Drivers\Digipay\Digipay;
use Shetabit\Multipay\Invoice;

class DigipayService
{
    use UUIDHelper;

    /** handle digipay delivery */
    public function digipayDelivery($orderGroup, $trackingCode)
    {
        try {
            // payment transaction
            $paymentTransaction = Transaction::where('transaction_id', $orderGroup->payment_transaction_uuid)->first();
            if ($paymentTransaction == null) return;

            // check for digipay gateway
            if ($paymentTransaction->payment_gateway != 'digipay') return;

            // set payment gateways configs
            $this->paymentGatewaysConfigs();

            // deliver
            $returnResponse = json_decode($paymentTransaction->return_response, true);
            $returnResponse = is_array($returnResponse) ? $returnResponse : [];
            if (array_key_exists('type', $returnResponse) && ((int)$returnResponse['type'] == 5 || (int)$returnResponse['type'] == 13)) {
                $invoice = new Invoice;
                $invoice->detail(['type' => $returnResponse['type']]);
                $invoice->detail(['invoiceNumber' => $trackingCode ?? (rand(1000000, 9999999) . '')]);
                $invoice->detail(['deliveryDate' => Carbon::now()->format('Y-m-d')]);
                $invoice->detail(['trackingCode' => $returnResponse['trackingCode']]);
                $invoice->detail(['products' => $this->productsName($orderGroup)]);
                $settings = config('payment.drivers.digipay');
                if (!$this->hasDeliveredOrder($orderGroup)) {
                    $digipayDriver = new Digipay($invoice, $settings);
                    $digipayDriver->deliver();
                }
            }
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /** handle digipay refund */
    public function digipayRefund($orderGroup, $paidPrice, $reason = '')
    {
        try {
            // payment transaction
            $paymentTransaction = Transaction::where('transaction_id', $orderGroup->payment_transaction_uuid)->first();
            if ($paymentTransaction == null) return;

            // check for digipay gateway
            if ($paymentTransaction->payment_gateway != 'digipay') return;

            // set payment gateways configs
            $this->paymentGatewaysConfigs();

            // customer
            $customer = $orderGroup->user;

            // refund
            $returnResponse = json_decode($paymentTransaction->return_response, true);
            $returnResponse = is_array($returnResponse) ? $returnResponse : [];
            if (array_key_exists('type', $returnResponse) && ((int)$returnResponse['type'] == 5 || (int)$returnResponse['type'] == 13)) {
                $invoice = new Invoice;
                $invoice->detail(['amount' => $paymentTransaction->amount]);
                $invoice->detail(['type' => $returnResponse['type']]);
                $invoice->detail(['providerId' => $returnResponse['providerId']]);
                $invoice->detail(['saleTrackingCode' => $returnResponse['trackingCode']]);
                $settings = config('payment.drivers.digipay');
                $digipayDriver = new Digipay($invoice, $settings);
                $digipayDriver->refundTransaction();

                // cancel all orders in order group
                $this->cancelOrderGroup($orderGroup, $reason);
            } else {
                $this->refundToWallet($customer, $paidPrice, $orderGroup);
            }
        } catch (\Exception $e) {
            throw $e;
        }
    }

    /** set payment gateways configs */
    protected function paymentGatewaysConfigs()
    {
        // payment gateways
        $paymentGatewaySetting = Setting::where('key', 'payment_gateway')->first();
        $paymentGateways = $paymentGatewaySetting ? unserialize($paymentGatewaySetting->value) : [];

        // set payment gateway driver configs
        foreach ($paymentGateways as $pg) {
            foreach ($pg['fields'] as $key => $field) {
                Config::set('payment.drivers.' . $pg['id'] . '.' . $key, $field['value']);
            }
        }
    }

    /** get order group products name */
    protected function productsName($orderGroup)
    {
        // variables
        $productsName = [];

        // looping orders in orderGroup
        foreach ($orderGroup->orders as $order) {
            foreach ($order->consignments()->where('status', '!=', 'cancelled')->get() as $consignment) {
                foreach ($consignment->consignmentItems()->where('status', '!=', 'cancelled')->get() as $consignmentItem) {
                    $productsName[] = $consignmentItem->title;
                }
            }
        }

        return $productsName;
    }

    /** refund to wallet */
    protected function refundToWallet($customer, $paidPrice, $orderGroup)
    {
        if ($customer->wallet) {
            $customer->wallet->update([
                'amount' => $customer->wallet->amount + $paidPrice,
            ]);
        }
        $customer->transactions()->create([
            'amount' => $paidPrice,
            'transaction_id' => $this->generateUniqueRandomNumber(9, \App\Models\Transaction::class, 'transaction_id'),
            'description' => __('messages.transaction_user_order_returned', ['order' => $orderGroup->uuid]),
            'type' => 'user_order_returned',
            'status' => 'accepted'
        ]);
    }

    /** cancel orderGroup */
    protected function cancelOrderGroup($orderGroup, $reason)
    {
        // looping orders in orderGroup
        foreach ($orderGroup->orders as $order) {
            // update order and consignment and consignmentItems inventory count and status / add cancellation reason
            $consignments = $order->consignments;
            foreach ($consignments as $consignment) {
                $consignment->update(['status' => 'cancelled']);
                foreach ($consignment->consignmentItems as $consignmentItem) {
                    // add inventory count
                    if ($consignmentItem->status != 'cancelled' && $consignmentItem->inventory != null) {
                        $consignmentItem->inventory->update([
                            'count' => $consignmentItem->inventory->count + $consignmentItem->count,
                        ]);
                    }

                    $consignmentItem->update([
                        'reason' => $reason,
                        'status' => 'cancelled',
                    ]);
                }
            }
            $order->update(['status' => 'cancelled']);
        }
    }

    /** check has delivered order in orderGroup or not */
    protected function hasDeliveredOrder($orderGroup): bool
    {
        $hasDeliveredOrder = false;

        // looping orders in orderGroup
        foreach ($orderGroup->orders as $order) {
            if (in_array($order->status, ['sent', 'delivered'])) {
                $hasDeliveredOrder = true;
            }
        }

        return $hasDeliveredOrder;
    }
}
