<?php

declare(strict_types=1);

namespace ApiModel\Shared\Validation;

use ApiModel\Shared\Exceptions\ValidationException;
use ApiModel\Shared\I18n\Translator;

final class CriticalInputValidator
{
    private const NOTE_MAX_LEN = 4000;

    /** @param array<string, mixed> $b */
    public static function loginBody(array $b, ?string $lang = null): void
    {
        $lang = Translator::normalizeLang($lang ?? (string) ($b['lang'] ?? 'es'));
        $fields = [];
        $email = trim((string) ($b['email'] ?? ''));
        $password = (string) ($b['password'] ?? '');

        if ($email === '' || filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
            $fields['email'] = Translator::get('validation.email_required', $lang);
        }
        if ($password === '') {
            $fields['password'] = Translator::get('validation.password_required', $lang);
        }
        if ($fields !== []) {
            throw new ValidationException(Translator::get('validation.login_invalid', $lang), ['fields' => $fields]);
        }
    }

    /** @param array<string, mixed> $b */
    public static function refreshBody(array $b, ?string $lang = null): void
    {
        $lang = Translator::normalizeLang($lang ?? (string) ($b['lang'] ?? 'es'));
        $token = trim((string) ($b['refresh_token'] ?? ''));
        if ($token === '' || strlen($token) < 16) {
            throw new ValidationException(Translator::get('validation.refresh_invalid', $lang), [
                'fields' => ['refresh_token' => Translator::get('validation.refresh_required', $lang)],
            ]);
        }
    }

    /** @param array<string, mixed> $b */
    public static function logoutBody(array $b, ?string $lang = null): void
    {
        $lang = Translator::normalizeLang($lang ?? (string) ($b['lang'] ?? 'es'));
        $token = trim((string) ($b['refresh_token'] ?? ''));
        if ($token === '') {
            throw new ValidationException(Translator::get('validation.invalid_data', $lang), [
                'fields' => ['refresh_token' => Translator::get('validation.refresh_required_short', $lang)],
            ]);
        }
    }

    /** @param array<string, mixed> $b */
    public static function forgotPasswordBody(array $b, ?string $lang = null): void
    {
        $lang = Translator::normalizeLang($lang ?? (string) ($b['lang'] ?? 'es'));
        $fields = [];
        $email = trim((string) ($b['email'] ?? ''));
        $inputLang = strtolower(trim((string) ($b['lang'] ?? 'es')));

        if ($email === '' || filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
            $fields['email'] = Translator::get('validation.email_required', $lang);
        }
        if (! in_array($inputLang, ['es', 'en'], true)) {
            $fields['lang'] = Translator::get('validation.lang_unsupported', $lang);
        }
        if ($fields !== []) {
            throw new ValidationException(Translator::get('validation.invalid_data', $lang), ['fields' => $fields]);
        }
    }

    /** @param array<string, mixed> $b */
    public static function resetPasswordBody(array $b, ?string $lang = null): void
    {
        $lang = Translator::normalizeLang($lang ?? (string) ($b['lang'] ?? 'es'));
        $fields = [];
        $email = trim((string) ($b['email'] ?? ''));
        $code = trim((string) ($b['code'] ?? ''));
        $newPassword = (string) ($b['new_password'] ?? '');

        if ($email === '' || filter_var($email, FILTER_VALIDATE_EMAIL) === false) {
            $fields['email'] = Translator::get('validation.email_required', $lang);
        }
        if ($code === '' || ! preg_match('/^\d{6}$/', $code)) {
            $fields['code'] = Translator::get('validation.code_six_digits', $lang);
        }
        $pwdErr = self::passwordPolicyError($newPassword, $lang);
        if ($pwdErr !== null) {
            $fields['new_password'] = $pwdErr;
        }
        if ($fields !== []) {
            throw new ValidationException(Translator::get('validation.reset_invalid', $lang), ['fields' => $fields]);
        }
    }

    /** @param array<string, mixed> $b */
    public static function createExternalClaimBody(array $b, ?string $lang = null): void
    {
        $lang = Translator::normalizeLang($lang ?? (string) ($b['lang'] ?? 'es'));
        $fields = [];

        $extId = trim((string) ($b['external_id'] ?? ''));
        if ($extId === '' || strlen($extId) > 128) {
            $fields['external_id'] = Translator::get('validation.external_id_required', $lang);
        }

        $insured = trim((string) ($b['insured_name'] ?? ''));
        if ($insured === '' || strlen($insured) > 255) {
            $fields['insured_name'] = Translator::get('validation.insured_name_required', $lang);
        }

        $incidentType = trim((string) ($b['incident_type'] ?? ''));
        if ($incidentType === '' || strlen($incidentType) > 128) {
            $fields['incident_type'] = Translator::get('validation.incident_type_required', $lang);
        }

        if (isset($b['incident_date']) && $b['incident_date'] !== '') {
            $raw = (string) $b['incident_date'];
            if (strtotime($raw) === false) {
                $fields['incident_date'] = Translator::get('validation.incident_date_invalid', $lang);
            }
        }

        $currency = trim((string) ($b['currency'] ?? 'USD'));
        if ($currency === '' || strlen($currency) > 8) {
            $fields['currency'] = Translator::get('validation.currency_required', $lang);
        }

        if (array_key_exists('reserve_amount', $b) && $b['reserve_amount'] !== '') {
            $reserve = $b['reserve_amount'];
            if (! is_numeric($reserve) || (float) $reserve < 0) {
                $fields['reserve_amount'] = Translator::get('validation.reserve_amount_invalid', $lang);
            }
        }

        if (isset($b['client_id']) && (string) $b['client_id'] !== '') {
            if (strlen((string) $b['client_id']) > 128) {
                $fields['client_id'] = Translator::get('validation.client_id_too_long', $lang);
            }
        }

        if (isset($b['vehicle'])) {
            if (! is_array($b['vehicle'])) {
                $fields['vehicle'] = Translator::get('validation.vehicle_must_be_object', $lang);
            } else {
                $v = $b['vehicle'];
                if (isset($v['year']) && $v['year'] !== '') {
                    $y = filter_var($v['year'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1900, 'max_range' => 2100]]);
                    if ($y === false) {
                        $fields['vehicle.year'] = Translator::get('validation.vehicle_year_invalid', $lang);
                    }
                }
            }
        }

        $docs = $b['documents'] ?? null;
        if (! is_array($docs) || $docs === []) {
            $fields['documents'] = Translator::get('validation.documents_required', $lang);
        } else {
            foreach ($docs as $i => $d) {
                if (! is_array($d)) {
                    $fields["documents.{$i}"] = Translator::get('validation.document_must_be_object', $lang);
                    continue;
                }
                $fn = trim((string) ($d['filename'] ?? ''));
                $path = trim((string) ($d['storage_path'] ?? ''));
                if ($fn === '' || strlen($fn) > 255) {
                    $fields["documents.{$i}.filename"] = Translator::get('validation.document_filename_required', $lang);
                }
                if ($path === '' || strlen($path) > 2048) {
                    $fields["documents.{$i}.storage_path"] = Translator::get('validation.document_storage_path_required', $lang);
                }
                if (isset($d['file_type']) && strlen((string) $d['file_type']) > 64) {
                    $fields["documents.{$i}.file_type"] = Translator::get('validation.document_file_type_too_long', $lang);
                }
            }
        }

        if ($fields !== []) {
            throw new ValidationException(Translator::get('validation.claim_invalid', $lang), ['fields' => $fields]);
        }
    }

    /**
     * @return null|string error message or null if OK
     */
    public static function optionalNote(mixed $note, ?string $lang = null): ?string
    {
        $lang = Translator::normalizeLang($lang);
        if ($note === null) {
            return null;
        }
        if (! is_string($note)) {
            throw new ValidationException(Translator::get('validation.note_invalid', $lang), [
                'fields' => ['note' => Translator::get('validation.note_must_be_text', $lang)],
            ]);
        }
        if (strlen($note) > self::NOTE_MAX_LEN) {
            throw new ValidationException(Translator::get('validation.note_too_long', $lang), [
                'fields' => ['note' => sprintf(Translator::get('validation.note_max_chars', $lang), self::NOTE_MAX_LEN)],
            ]);
        }

        return $note === '' ? null : $note;
    }

    private static function passwordPolicyError(string $password, string $lang): ?string
    {
        $len = strlen($password);
        if ($len < 8) {
            return Translator::get('validation.password_min', $lang);
        }
        if ($len > 128) {
            return Translator::get('validation.password_max', $lang);
        }
        if (! preg_match('/[[:alpha:]]/u', $password) || ! preg_match('/\p{N}/u', $password)) {
            return Translator::get('validation.password_letter_number', $lang);
        }

        return null;
    }
}
