<?php
/// file: app/Core/Validator.php

declare(strict_types=1);

namespace App\Core;

final class Validator
{
    /** @param array<string, mixed> $data */
    public function __construct(private array $data)
    {
    }

    /**
     * @param array<string, string> $rules e.g. ['email' => 'required|email']
     * @param array<string, string> $messages
     * @return array<string, list<string>>
     */
    public function validate(array $rules, array $messages = []): array
    {
        $errors = [];
        foreach ($rules as $field => $ruleString) {
            $ruleList = array_map('trim', explode('|', $ruleString));
            $value = $this->data[$field] ?? null;
            foreach ($ruleList as $rule) {
                $param = null;
                if (str_contains($rule, ':')) {
                    [$rule, $param] = explode(':', $rule, 2);
                }
                $msg = $this->check($field, $value, $rule, $param, $messages);
                if ($msg !== null) {
                    $errors[$field][] = $msg;
                    break;
                }
            }
        }
        return $errors;
    }

    private function check(string $field, mixed $value, string $rule, ?string $param, array $messages): ?string
    {
        $label = $field;
        $custom = $messages[$field . '.' . $rule] ?? null;
        return match ($rule) {
            'required' => ($value === null || $value === '' || (is_string($value) && trim($value) === ''))
                ? ($custom ?? "{$label} is required.")
                : null,
            'email' => (is_string($value) && filter_var($value, FILTER_VALIDATE_EMAIL))
                ? null
                : ($custom ?? 'Invalid email.'),
            'max' => (is_string($value) && $param !== null && mb_strlen($value) > (int) $param)
                ? ($custom ?? "{$label} must be at most {$param} characters.")
                : null,
            'min' => (is_string($value) && $param !== null && mb_strlen($value) < (int) $param)
                ? ($custom ?? "{$label} must be at least {$param} characters.")
                : null,
            default => null,
        };
    }
}
