<?php

namespace Espo\Custom\Classes\FormulaFunctions\IbanGroup;

use Espo\Core\Formula\Functions\BaseFunction;
use Espo\Core\Formula\ArgumentList;

/**
 * IBAN-Validierung mit Modulo-97-Algorithmus (ISO 13616)
 * Mathematische Prüfung ohne externe Datenbank
 */
class ValidateType extends BaseFunction
{
    public function process(ArgumentList $args)
    {
        if (count($args) < 1) {
            return false;
        }

        $iban = $this->evaluate($args[0]);
        
        if (!$iban || !is_string($iban)) {
            return false;
        }

        // Leerzeichen entfernen und in Großbuchstaben umwandeln
        $iban = strtoupper(str_replace(' ', '', trim($iban)));

        // Basis-Format prüfen: Mindestens 15, maximal 34 Zeichen
        $length = strlen($iban);
        if ($length < 15 || $length > 34) {
            return false;
        }

        // Format prüfen: 2 Buchstaben (Ländercode) + 2 Ziffern (Prüfziffer) + Rest alphanumerisch
        if (!preg_match('/^[A-Z]{2}[0-9]{2}[A-Z0-9]+$/', $iban)) {
            return false;
        }

        // Ländercode extrahieren und Länge prüfen (optionale erweiterte Prüfung)
        $countryCode = substr($iban, 0, 2);
        $expectedLength = $this->getIbanLength($countryCode);
        if ($expectedLength && $length !== $expectedLength) {
            return false;
        }

        // Modulo-97-Algorithmus (ISO 13616)
        // 1. IBAN umstellen: ersten 4 Zeichen ans Ende
        $rearranged = substr($iban, 4) . substr($iban, 0, 4);

        // 2. Buchstaben in Zahlen umwandeln (A=10, B=11, ..., Z=35)
        $numericString = '';
        for ($i = 0; $i < strlen($rearranged); $i++) {
            $char = $rearranged[$i];
            if (ctype_alpha($char)) {
                // A=65 -> 10, B=66 -> 11, ..., Z=90 -> 35
                $numericString .= (string)(ord($char) - 55);
            } else {
                $numericString .= $char;
            }
        }

        // 3. Modulo 97 berechnen (schrittweise wegen großer Zahlen)
        $remainder = 0;
        for ($i = 0; $i < strlen($numericString); $i++) {
            $remainder = ($remainder * 10 + (int)$numericString[$i]) % 97;
        }

        // 4. Rest muss genau 1 sein für gültige IBAN
        return $remainder === 1;
    }

    /**
     * Gibt die erwartete Länge einer IBAN für einen Ländercode zurück
     * Quelle: ISO 13616 / SWIFT IBAN Registry
     */
    private function getIbanLength(string $countryCode): ?int
    {
        $lengths = [
            'AD' => 24, 'AE' => 23, 'AL' => 28, 'AT' => 20, 'AZ' => 28,
            'BA' => 20, 'BE' => 16, 'BG' => 22, 'BH' => 22, 'BR' => 29,
            'BY' => 28, 'CH' => 21, 'CR' => 22, 'CY' => 28, 'CZ' => 24,
            'DE' => 22, 'DK' => 18, 'DO' => 28, 'EE' => 20, 'EG' => 29,
            'ES' => 24, 'FI' => 18, 'FO' => 18, 'FR' => 27, 'GB' => 22,
            'GE' => 22, 'GI' => 23, 'GL' => 18, 'GR' => 27, 'GT' => 28,
            'HR' => 21, 'HU' => 28, 'IE' => 22, 'IL' => 23, 'IS' => 26,
            'IT' => 27, 'JO' => 30, 'KW' => 30, 'KZ' => 20, 'LB' => 28,
            'LC' => 32, 'LI' => 21, 'LT' => 20, 'LU' => 20, 'LV' => 21,
            'MC' => 27, 'MD' => 24, 'ME' => 22, 'MK' => 19, 'MR' => 27,
            'MT' => 31, 'MU' => 30, 'NL' => 18, 'NO' => 15, 'PK' => 24,
            'PL' => 28, 'PS' => 29, 'PT' => 25, 'QA' => 29, 'RO' => 24,
            'RS' => 22, 'SA' => 24, 'SE' => 24, 'SI' => 19, 'SK' => 24,
            'SM' => 27, 'TN' => 24, 'TR' => 26, 'UA' => 29, 'VA' => 22,
            'VG' => 24, 'XK' => 20,
        ];

        return $lengths[$countryCode] ?? null;
    }
}
