<?php

namespace App\Repositories\Shop;

use App\Models\Shop\Invoice;
use App\Models\Shop\InvoicePayment;
use App\Repositories\Core\DateTime;
use Bnb\PayboxGateway\Requests\Paybox\AuthorizationWithCapture;
use Bnb\PayboxGateway\Requests\PayboxDirect\Capture;
use Bnb\PayboxGateway\Responses\Exceptions\InvalidSignature;
use Bnb\PayboxGateway\Responses\Paybox\Verify;
use Illuminate\Support\Facades\App;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class Paybox
{
    public static function makeAuthorizationRequest($order, $customer_email = 'test@example.com')
    {
        $authorizationRequest = App::make(AuthorizationWithCapture::class);
        $invoiceId = $order->invoice->id;

        return $authorizationRequest->setAmount($order->total_shipped)->setCustomerEmail($customer_email)
            ->setPaymentNumber($invoiceId)->send('paybox.send');
    }

    public static function verifyPayment($invoiceId)
    {
        $invoice = Invoices::get($invoiceId, ['order']);

        if (! $invoice) {
            Log::warning('Paybox callback received for unknown invoice', [
                'invoice_id' => $invoiceId,
                'payload' => request()->all(),
            ]);

            return false;
        }

        $payboxVerify = App::make(Verify::class);

        try {
            $isSuccessful = $payboxVerify->isSuccess($invoice->total_shipped);
        } catch (InvalidSignature $e) {
            Log::alert('Invalid payment signature detected', [
                'invoice_id' => $invoiceId,
                'payload' => request()->except('signature'),
            ]);

            return false;
        }

        if (! $isSuccessful) {
            Log::warning('Paybox payment verification failed', [
                'invoice_id' => $invoiceId,
                'response_code' => $payboxVerify->getResponseCode(),
                'payload' => request()->except('signature'),
            ]);

            return false;
        }

        return self::finalizeInvoicePayment($invoice);
    }

    protected static function finalizeInvoicePayment(Invoice $invoice)
    {
        $order = $invoice->order;

        if (! $order) {
            Log::error('Paybox payment cannot be finalized: missing related order', [
                'invoice_id' => $invoice->id,
            ]);

            return false;
        }

        $request = request();
        $referenceParts = array_filter([
            $request->input('call_number'),
            $request->input('transaction_number'),
        ]);
        $reference = $referenceParts ? implode('-', $referenceParts) : $request->input('authorization_number');
        if (! $reference) {
            $reference = 'paybox-'.$invoice->id;
        }

        $payload = $request->except('signature');
        $existingPayment = InvoicePayment::where('invoice_id', $invoice->id)
            ->where('reference', $reference)
            ->first();
        $shouldNotify = false;
        $validatedTotal = InvoicePayment::where('invoice_id', $invoice->id)
            ->validated()
            ->sum('amount');

        if (! $existingPayment && (float) $validatedTotal >= (float) $invoice->total_shipped) {
            Log::info('Paybox payment ignored: invoice already fully settled', [
                'invoice_id' => $invoice->id,
                'order_id' => $order->id,
                'reference' => $reference,
            ]);

            return true;
        }

        $isCancelled = (int) $order->status === 4;

        DB::transaction(function () use ($invoice, $order, $reference, $payload, $existingPayment, &$shouldNotify, $isCancelled) {
            $attributes = [
                'payment_type' => 1,
                'amount' => $invoice->total_shipped,
                'date' => DateTime::getDate(),
                'data' => json_encode($payload, JSON_UNESCAPED_UNICODE),
                'validated' => 1,
            ];

            if ($existingPayment) {
                $previousValidationState = (int) ($existingPayment->validated ?? 0);
                $existingPayment->fill($attributes);

                if ($existingPayment->isDirty()) {
                    $existingPayment->save();
                }

                if ($previousValidationState !== 1 && (int) $existingPayment->validated === 1) {
                    $shouldNotify = true;
                }
            } else {
                InvoicePayment::create($attributes + [
                    'invoice_id' => $invoice->id,
                    'reference' => $reference,
                ]);
                $shouldNotify = true;
            }

            Invoices::checkPayments($invoice->id);

            if (! $isCancelled) {
                $paidStatus = Orders::getStatusByName('Préparation');
                if ($paidStatus !== '' && (int) $order->status !== (int) $paidStatus) {
                    $order->status = $paidStatus;
                    $order->save();
                }
            }
        });

        if ($isCancelled && $shouldNotify) {
            Log::warning('Paybox payment received on cancelled order', [
                'order_id' => $order->id,
                'order_ref' => $order->ref,
                'invoice_id' => $invoice->id,
                'reference' => $reference,
            ]);
            OrderMails::sendCancelledOrderPaymentAlert($order->id, $reference);
        } elseif ($shouldNotify) {
            try {
                OrderMails::sendOrderConfirmed($order->id);
            } catch (\Throwable $exception) {
                Log::error('Unable to send order confirmation email after Paybox payment', [
                    'order_id' => $order->id,
                    'invoice_id' => $invoice->id,
                    'exception' => $exception->getMessage(),
                ]);
            }
        }

        Log::info('Paybox payment finalized successfully', [
            'invoice_id' => $invoice->id,
            'order_id' => $order->id,
            'reference' => $reference,
            'notified' => $shouldNotify,
        ]);

        return true;
    }

    public static function getPreviousAuthorizedRequest($request)
    {
        $payment = InvoicePayment::where('number', $request->input('order_number'))->firstOrFail();
        $captureRequest = App::make(Capture::class);
        $response = $captureRequest->setAmount($payment->amount)
            ->setPayboxCallNumber($payment->call_number)
            ->setPayboxTransactionNumber($payment->transaction_number)
            ->setDayRequestNumber(2)
            ->send();

        if ($response->isSuccess()) {
            // process order here
        }
    }
}
