<?php
/**
 * Input Validation Helper
 * Rule: NEVER trust user input.
 */

class Validator {

    private array $errors = [];

    /**
     * Validate input data against rules.
     * @param array $data Input data
     * @param array $rules ['field' => 'required|string|max:255', ...]
     * @return bool
     */
    public function validate(array $data, array $rules): bool {
        $this->errors = [];

        foreach ($rules as $field => $ruleString) {
            $ruleList = explode('|', $ruleString);
            $value = $data[$field] ?? null;

            foreach ($ruleList as $rule) {
                $params = [];
                if (strpos($rule, ':') !== false) {
                    [$rule, $paramStr] = explode(':', $rule, 2);
                    $params = explode(',', $paramStr);
                }

                $this->applyRule($field, $value, $rule, $params, $data);
            }
        }

        return empty($this->errors);
    }

    private function applyRule(string $field, $value, string $rule, array $params, array $data): void {
        switch ($rule) {
            case 'required':
                if ($value === null || $value === '' || $value === []) {
                    $this->errors[$field][] = "{$field} is required.";
                }
                break;

            case 'string':
                if ($value !== null && !is_string($value)) {
                    $this->errors[$field][] = "{$field} must be a string.";
                }
                break;

            case 'numeric':
                if ($value !== null && $value !== '' && !is_numeric($value)) {
                    $this->errors[$field][] = "{$field} must be numeric.";
                }
                break;

            case 'integer':
                if ($value !== null && $value !== '' && filter_var($value, FILTER_VALIDATE_INT) === false) {
                    $this->errors[$field][] = "{$field} must be an integer.";
                }
                break;

            case 'min':
                if (is_string($value)) {
                    if (strlen($value) < $params[0]) {
                        $this->errors[$field][] = "{$field} must be at least {$params[0]} characters.";
                    }
                } elseif (is_numeric($value)) {
                    if ($value < $params[0]) {
                        $this->errors[$field][] = "{$field} must be at least {$params[0]}.";
                    }
                }
                break;

            case 'max':
                if (is_string($value)) {
                    if (strlen($value) > $params[0]) {
                        $this->errors[$field][] = "{$field} must not exceed {$params[0]} characters.";
                    }
                } elseif (is_numeric($value)) {
                    if ($value > $params[0]) {
                        $this->errors[$field][] = "{$field} must not exceed {$params[0]}.";
                    }
                }
                break;

            case 'in':
                if ($value !== null && $value !== '' && !in_array((string)$value, $params)) {
                    $this->errors[$field][] = "{$field} must be one of: " . implode(', ', $params);
                }
                break;

            case 'date':
                if ($value !== null && $value !== '') {
                    $d = DateTime::createFromFormat('Y-m-d', $value);
                    if (!$d || $d->format('Y-m-d') !== $value) {
                        $this->errors[$field][] = "{$field} must be a valid date (YYYY-MM-DD).";
                    }
                }
                break;

            case 'nullable':
                // Allow null values - skip further validation if null
                if ($value === null || $value === '') {
                    return;
                }
                break;

            case 'positive':
                if (is_numeric($value) && $value <= 0) {
                    $this->errors[$field][] = "{$field} must be positive.";
                }
                break;

            case 'non_negative':
                if (is_numeric($value) && $value < 0) {
                    $this->errors[$field][] = "{$field} must not be negative.";
                }
                break;
        }
    }

    public function getErrors(): array {
        return $this->errors;
    }

    /**
     * Static shorthand: validate or throw
     */
    public static function check(array $data, array $rules): void {
        $v = new self();
        if (!$v->validate($data, $rules)) {
            Response::validationError('Validation failed', $v->getErrors());
        }
    }
}
