<?php
/**
 * Copyright © Simplyin. All rights reserved.
 */
declare(strict_types=1);

namespace Simplyin\Payment\Service;

/**
 * Single source of truth for the SimplyIN payment display contract.
 *
 * The pair (payment_method, payment_method_title) sent to the SimplyIN API must
 * be identical whether it is composed on the live checkout path
 * (OrderDataEnricher / CreateOrderDataEnricher) or on the reconciliation/sync
 * path (OrderPayloadBuilder). Keeping the rules here — as a pure, dependency-free
 * class — prevents the call sites from drifting again. Mirrors the equivalent
 * PrestaShop 8 plugin implementation.
 *
 * Case A (SimplyIN payment):
 *   payment_method       = "Simply.IN · {Provider}" (U+00B7 middle dot;
 *                          empty or self-referential provider -> "Simply.IN")
 *   payment_method_title = card + last4 -> "**** 1234"
 *                          card, no usable last4 -> "Card"
 *                          BLIK -> "BLIK"
 *                          other -> method type name
 *
 * Case B (native shop payment method):
 *   payment_method       = the method's display title (code fallback)
 *   payment_method_title = the method code, only when genuinely different
 *                          from payment_method (never a duplicate; else empty)
 *
 * PCI: never render the "0000" placeholder produced for the payment payload
 * (ProcessPayment::buildPaymentOnlyMethodData); a card without a real last4
 * falls back to "Card".
 */
class PaymentDisplayFormatter
{
    /**
     * Product brand used as the payment_method prefix.
     */
    private const BRAND = 'Simply.IN';

    /**
     * Separator between brand and provider: space + U+00B7 (middle dot) + space.
     */
    private const SEPARATOR = " \u{00B7} ";

    /**
     * Backend payment-method identifiers (and live-path target types) that
     * represent a card instrument.
     *
     * @var string[]
     */
    private const CARD_METHODS = [
        'card',
        'cc',
        'credit_card',
        'debit_card',
        'saved_card',
        'add_card',
        'add-card',
        'one_click',
    ];

    /**
     * Compose the Case A payment_method value.
     *
     * @param string $providerName Provider display name (e.g. "PayU"); empty or
     *                             self-referential ("SimplyIN") collapses to the brand.
     * @return string
     */
    public function composeSimplyinMethod(string $providerName): string
    {
        $provider = trim($providerName);

        // Collapse an empty or self-referential provider to the bare brand so we
        // never emit "Simply.IN · SimplyIN" (Magento defaults the provider type
        // to "simplyin" when the real provider is not yet known).
        $normalized = strtolower(str_replace(['.', ' '], '', $provider));
        if ($provider === '' || $normalized === 'simplyin') {
            return self::BRAND;
        }

        return self::BRAND . self::SEPARATOR . $provider;
    }

    /**
     * Compose the Case A payment_method_title value.
     *
     * @param string $methodId  Backend method id or live-path target type
     *                          (e.g. "card", "blik", "add_card", "pbl").
     * @param string $last4     Last-4 card digits; noisy input is normalized.
     * @param string $typeLabel Human method-type label used for non-card/non-BLIK
     *                          methods (e.g. "PayPal", "Bank Transfer").
     * @return string
     */
    public function composeSimplyinTitle(string $methodId, string $last4, string $typeLabel): string
    {
        $key = strtolower(trim($methodId));

        // A last-4 value only ever accompanies a card instrument, so treat its
        // presence as a card signal when the method id is missing (partial
        // additional_information / legacy callers).
        $isCard = in_array($key, self::CARD_METHODS, true)
            || ($key === '' && trim($last4) !== '');

        if ($isCard) {
            $digits = $this->normalizeLast4($last4);

            return $digits !== '' ? '**** ' . $digits : 'Card';
        }

        if ($key === 'blik') {
            return 'BLIK';
        }

        $label = trim($typeLabel);

        return $label !== '' ? $label : trim($methodId);
    }

    /**
     * Compose the Case B (native shop payment method) display pair.
     *
     * The payment_method value is the method's human title (falling back to the
     * code); payment_method_title carries the method code only when it is
     * genuinely different from payment_method — never a duplicate (empty otherwise).
     *
     * @param string $title      Native method display title (may be empty).
     * @param string $methodCode Native method code (e.g. "checkmo").
     * @return array [string $paymentMethod, string $paymentMethodTitle]
     */
    public function composeNativeDisplay(string $title, string $methodCode): array
    {
        $title = trim($title);
        $code = trim($methodCode);

        $paymentMethod = $title !== '' ? $title : $code;
        $paymentMethodTitle = ($code !== '' && strcasecmp($code, $paymentMethod) !== 0) ? $code : '';

        return [$paymentMethod, $paymentMethodTitle];
    }

    /**
     * Normalize a possibly noisy last-4 value for display.
     *
     * Strips non-digits and keeps the trailing four digits; returns '' when
     * fewer than four digits remain (a partial value is not a usable last4)
     * or when the value is the PCI "0000" placeholder, so the caller renders
     * "Card".
     *
     * @param string $last4
     * @return string
     */
    private function normalizeLast4(string $last4): string
    {
        $digits = preg_replace('/\D/', '', $last4) ?? '';
        $tail = strlen($digits) >= 4 ? substr($digits, -4) : '';

        return $tail !== '0000' ? $tail : '';
    }
}
