<?php

declare(strict_types=1);

namespace OCA\Mitgliederverwaltung\Validation;

/**
 * Validates and normalizes phone numbers to E.164 format.
 *
 * Accepted input formats:
 * - +49176123456 (already E.164)
 * - 0049176123456 (international with 00 prefix)
 * - 0176123456 (German local, auto-prefixed with +49)
 *
 * Part of Issue #25.
 */
class PhoneValidator {

    /** E.164 pattern: + followed by 1-15 digits */
    private const E164_PATTERN = '/^\+[1-9][0-9]{1,14}$/';

    /**
     * Validate that a phone number is in E.164 format.
     *
     * @param string $number The phone number to validate
     * @return bool True if the number is valid E.164
     */
    public static function isValid(string $number): bool {
        $cleaned = self::clean($number);
        return (bool)preg_match(self::E164_PATTERN, $cleaned);
    }

    /**
     * Normalize a phone number to E.164 format.
     *
     * Handles common German input formats:
     * - Strips spaces, dashes, parentheses, slashes
     * - Converts 00XX prefix to +XX
     * - Converts 0XX (German local) to +49XX
     *
     * @param string $number Raw phone number input
     * @return string Normalized number in E.164 format
     */
    public static function normalize(string $number): string {
        $cleaned = self::clean($number);

        // Already in E.164 format
        if (str_starts_with($cleaned, '+')) {
            return $cleaned;
        }

        // International 00 prefix -> +
        if (str_starts_with($cleaned, '00')) {
            return '+' . substr($cleaned, 2);
        }

        // German local number (starts with 0) -> +49
        if (str_starts_with($cleaned, '0')) {
            return '+49' . substr($cleaned, 1);
        }

        // Last resort: prepend + and hope for the best
        return '+' . $cleaned;
    }

    /**
     * Validate and normalize a phone number.
     * Returns the normalized number or null if invalid.
     *
     * @param string $number Raw phone number input
     * @return string|null Normalized E.164 number, or null if invalid
     */
    public static function validateAndNormalize(string $number): ?string {
        $normalized = self::normalize($number);
        return self::isValid($normalized) ? $normalized : null;
    }

    /**
     * Get a human-readable validation error message.
     *
     * @param string $number The invalid phone number
     * @return string Error description in German
     */
    public static function getErrorMessage(string $number): string {
        $cleaned = self::clean($number);

        if (strlen($cleaned) === 0) {
            return 'Telefonnummer darf nicht leer sein.';
        }

        $normalized = self::normalize($number);

        if (!str_starts_with($normalized, '+')) {
            return 'Telefonnummer muss mit + oder 0 beginnen.';
        }

        $digits = substr($normalized, 1);
        if (!ctype_digit($digits)) {
            return 'Telefonnummer darf nur Ziffern enthalten (nach dem +).';
        }

        $len = strlen($digits);
        if ($len < 2) {
            return 'Telefonnummer ist zu kurz (mindestens 3 Zeichen inkl. +).';
        }
        if ($len > 15) {
            return 'Telefonnummer ist zu lang (maximal 15 Ziffern nach +).';
        }

        return 'Telefonnummer hat ein ungueltiges Format. Erwartet: +49...';
    }

    /**
     * Strip all non-essential characters from a phone number.
     */
    private static function clean(string $number): string {
        // Remove spaces, dashes, parentheses, slashes, dots
        return preg_replace('/[\s\-\(\)\/\.]/', '', trim($number));
    }
}
