<?php
/**
 * @package Hafenstudios\Leadlotse
 */

namespace Hafenstudios\Leadlotse\Fields;

defined( 'ABSPATH' ) || exit;

/**
 * Reine, testbare Plausibilitaetspruefung fuer Telefonnummern (Inhaber-Wunsch,
 * L-F1). Keine WordPress-Abhaengigkeiten, damit die Logik ohne WP getestet werden
 * kann. Die verbindliche Feldpruefung in PhoneField ruft diese Klasse auf und
 * reicht das Ergebnis anschliessend durch den Filter leadlotse_phone_validation,
 * damit spaeter ein externer Nummer-vergeben-Dienst (HLR-Lookup, Pro) andocken
 * kann, ohne die Basis-Logik zu aendern.
 *
 * Grundsatz: Diese Pruefung stellt nur die grobe Form sicher (E.164-Rahmen). Ob
 * eine Nummer wirklich vergeben ist, kann sie bewusst nicht wissen.
 */
final class PhoneValidator {

	/**
	 * Untere Grenze der Ziffernzahl (nationale Kurznummern ausgenommen).
	 */
	public const MIN_DIGITS = 6;

	/**
	 * Obere Grenze der Ziffernzahl (E.164 erlaubt maximal 15 Ziffern).
	 */
	public const MAX_DIGITS = 15;

	/**
	 * Normalisiert eine Rohnummer auf eine kompakte Form: entfernt Leerzeichen,
	 * Klammern, Bindestriche, Punkte und Schraegstriche. Ein fuehrendes Plus bleibt
	 * erhalten; eine fuehrende internationale 00-Vorwahl wird zu Plus.
	 *
	 * @param string $raw Rohe Eingabe.
	 * @return string Normalisierte Nummer (z.B. "+49171...") oder Leerstring.
	 */
	public static function normalize( string $raw ): string {
		$raw      = trim( $raw );
		$has_plus = 0 === strncmp( $raw, '+', 1 );
		$digits   = (string) preg_replace( '/\D/', '', $raw );

		if ( '' === $digits ) {
			return '';
		}

		if ( $has_plus ) {
			return '+' . $digits;
		}

		// Fuehrende 00 als internationale Wahl auf Plus normalisieren.
		if ( 0 === strncmp( $digits, '00', 2 ) ) {
			$rest = substr( $digits, 2 );
			return '' === $rest ? '' : '+' . $rest;
		}

		return $digits;
	}

	/**
	 * Prueft eine bereits normalisierte Nummer auf grobe Plausibilitaet.
	 *
	 * @param string $normalized Normalisierte Nummer (siehe normalize()).
	 * @return bool True, wenn die Nummer plausibel wirkt.
	 */
	public static function is_plausible( string $normalized ): bool {
		if ( '' === $normalized ) {
			return false;
		}

		// Nach einem optionalen fuehrenden Plus duerfen nur Ziffern stehen.
		$digits = 0 === strncmp( $normalized, '+', 1 ) ? substr( $normalized, 1 ) : $normalized;
		if ( '' === $digits || ! ctype_digit( $digits ) ) {
			return false;
		}

		$len = strlen( $digits );
		if ( $len < self::MIN_DIGITS || $len > self::MAX_DIGITS ) {
			return false;
		}

		// Fake-Muster, Stufe 1: Die GANZE Nummer ist eine einzige Wiederholung
		// (111111) oder eine einzige aufsteigende Kette (123456) - egal wie kurz.
		if ( 1 === preg_match( '/^(\d)\1+$/', $digits ) || self::has_ascending_run( $digits, strlen( $digits ) ) ) {
			return false;
		}

		// Fake-Muster, Stufe 2: sehr lange Wiederholungen/Ketten IN der Nummer
		// (ab 8 Stellen). Bewusst nicht schaerfer: echte Nummern enthalten legitim
		// kuerzere Sequenzen (Berliner 234567x, 0800-Wunschnummern mit 123456) -
		// ein faelschlich abgewiesener echter Lead kostet mehr als eine
		// durchgerutschte Fantasienummer.
		if ( 1 === preg_match( '/(\d)\1{7,}/', $digits ) ) {
			return false;
		}
		if ( self::has_ascending_run( $digits, 8 ) ) {
			return false;
		}

		return true;
	}

	/**
	 * Bequemer Gesamtcheck: normalisiert und prueft in einem Schritt.
	 *
	 * @param string $raw Rohe Eingabe.
	 * @return bool
	 */
	public static function is_valid( string $raw ): bool {
		return self::is_plausible( self::normalize( $raw ) );
	}

	/**
	 * Enthaelt die Ziffernfolge eine lueckenlos aufsteigende Kette (jede Ziffer um
	 * eins groesser als die vorige) mit mindestens $min aufeinanderfolgenden Stellen?
	 *
	 * @param string $digits Reine Ziffernfolge.
	 * @param int    $min    Mindestlaenge der Kette.
	 * @return bool
	 */
	private static function has_ascending_run( string $digits, int $min ): bool {
		$run = 1;
		$max = strlen( $digits );
		for ( $i = 1; $i < $max; $i++ ) {
			$prev = (int) $digits[ $i - 1 ];
			$curr = (int) $digits[ $i ];
			if ( $curr === $prev + 1 ) {
				++$run;
				if ( $run >= $min ) {
					return true;
				}
			} else {
				$run = 1;
			}
		}
		return false;
	}
}
