<?php

namespace App\Services;

use App\Models\Transaction;

class TransactionReceiptService
{
    /**
     * @return array<string,mixed>
     */
    public function payload(Transaction $transaction): array
    {
        $transaction->loadMissing([
            'category:id,name,slug',
            'plan:id,name,network_id',
            'plan.network:id,name,slug',
            'latestVendorLog',
        ]);

        $meta = (array) ($transaction->meta_json ?? []);
        $categorySlug = (string) ($transaction->category?->slug ?? '');
        $response = (array) ($transaction->latestVendorLog?->response_json ?? []);
        $extras = $this->extractExtras($categorySlug, $meta, $response);
        $networkName = $transaction->plan?->network?->name;

        if (! $networkName) {
            $networkSlug = trim((string) data_get($meta, 'network_slug', ''));
            $networkName = $networkSlug !== '' ? strtoupper($networkSlug) : null;
        }

        return [
            'id' => (int) $transaction->id,
            'status' => (string) $transaction->status,
            'internal_reference' => (string) $transaction->internal_reference,
            'vendor_reference' => $transaction->vendor_reference,
            'amount_charged' => round((float) ($transaction->amount_charged ?? 0), 2),
            'gross_amount' => $transaction->gross_amount !== null ? round((float) $transaction->gross_amount, 2) : null,
            'discount_amount' => $transaction->discount_amount !== null ? round((float) $transaction->discount_amount, 2) : null,
            'wallet_debit_amount' => $transaction->wallet_debit_amount !== null ? round((float) $transaction->wallet_debit_amount, 2) : null,
            'cashback_debit_amount' => $transaction->cashback_debit_amount !== null ? round((float) $transaction->cashback_debit_amount, 2) : null,
            'category_name' => $transaction->category?->name,
            'category_slug' => $categorySlug !== '' ? $categorySlug : null,
            'plan_name' => $transaction->plan?->name,
            'network_name' => $networkName,
            'phone' => data_get($meta, 'phone') ?? data_get($meta, 'mobile_number'),
            'created_at' => optional($transaction->created_at)->toDateTimeString(),
            'meter_token' => $extras['meter_token'],
            'utility_units' => $extras['utility_units'],
            'utility_customer_name' => $extras['utility_customer_name'],
            'epin_pins' => $extras['epin_pins'],
        ];
    }

    /**
     * @return array{
     *     meter_token:?string,
     *     utility_units:?string,
     *     utility_customer_name:?string,
     *     epin_pins:array<int,string>
     * }
     */
    protected function extractExtras(string $categorySlug, array $meta, array $response): array
    {
        $meterToken = null;
        $utilityUnits = null;
        $utilityCustomerName = null;
        $epinPins = [];

        if ($categorySlug === 'electricity') {
            $meterToken = $this->toNonEmptyString(
                data_get($response, 'token')
                    ?? data_get($response, 'Token')
                    ?? data_get($response, 'content.token')
                    ?? data_get($response, 'content.Token')
                    ?? data_get($response, 'content.tokens')
                    ?? data_get($response, 'content.transactions.token')
                    ?? data_get($response, 'content.transactions.Token')
                    ?? data_get($response, 'data.token')
                    ?? data_get($response, 'data.Token')
                    ?? data_get($meta, 'meter_token')
                    ?? null
            );

            $utilityUnits = $this->toNonEmptyString(
                data_get($response, 'units')
                    ?? data_get($response, 'unitsPurchased')
                    ?? data_get($response, 'units_purchased')
                    ?? data_get($response, 'content.units')
                    ?? data_get($response, 'content.unitsPurchased')
                    ?? data_get($response, 'content.transactions.units')
                    ?? data_get($response, 'content.transactions.unitsPurchased')
                    ?? data_get($meta, 'utility_units')
                    ?? null
            );

            $utilityCustomerName = $this->toNonEmptyString(
                data_get($response, 'content.Customer_Name')
                    ?? data_get($response, 'content.customer_name')
                    ?? data_get($response, 'content.transactions.customer_name')
                    ?? data_get($response, 'name')
                    ?? data_get($meta, 'customer_name')
                    ?? null
            );
        }

        if ($categorySlug === 'epin') {
            $epinPins = $this->extractEpinPins($response);
        }

        return [
            'meter_token' => $meterToken,
            'utility_units' => $utilityUnits,
            'utility_customer_name' => $utilityCustomerName,
            'epin_pins' => $epinPins,
        ];
    }

    /**
     * @return array<int,string>
     */
    protected function extractEpinPins(array $response): array
    {
        $candidates = [
            data_get($response, 'pins'),
            data_get($response, 'pin'),
            data_get($response, 'pin_code'),
            data_get($response, 'content.pins'),
            data_get($response, 'content.pin'),
            data_get($response, 'content.pin_code'),
            data_get($response, 'content.transactions.pins'),
            data_get($response, 'content.transactions.pin'),
            data_get($response, 'data.pins'),
            data_get($response, 'data.pin'),
            data_get($response, 'transaction.pins'),
            data_get($response, 'transaction.pin'),
        ];

        $pins = [];

        foreach ($candidates as $candidate) {
            if ($candidate === null || $candidate === '') {
                continue;
            }

            if (is_array($candidate)) {
                foreach ($candidate as $entry) {
                    $formatted = $this->formatPinEntry($entry);
                    if ($formatted !== null) {
                        $pins[] = $formatted;
                    }
                }

                continue;
            }

            $formatted = $this->formatPinEntry($candidate);
            if ($formatted !== null) {
                $pins[] = $formatted;
            }
        }

        return array_values(array_unique($pins));
    }

    protected function formatPinEntry(mixed $entry): ?string
    {
        if (is_array($entry)) {
            $serial = $this->toNonEmptyString(
                data_get($entry, 'serial')
                    ?? data_get($entry, 'serial_no')
                    ?? data_get($entry, 'serialNumber')
                    ?? null
            );
            $pin = $this->toNonEmptyString(
                data_get($entry, 'pin')
                    ?? data_get($entry, 'pin_code')
                    ?? data_get($entry, 'code')
                    ?? data_get($entry, 'value')
                    ?? null
            );

            if ($serial && $pin) {
                return "{$serial}: {$pin}";
            }

            return $pin ?: $serial;
        }

        return $this->toNonEmptyString($entry);
    }

    protected function toNonEmptyString(mixed $value): ?string
    {
        if ($value === null || is_array($value)) {
            return null;
        }

        $string = trim((string) $value);

        return $string !== '' ? $string : null;
    }
}
