<?php
declare(strict_types=1);

namespace App\Core;

use App\Config\Database;
use PDO;

/**
 * Input Validation Engine
 */
class Validator
{
    private array $errors = [];

    /**
     * Validate data against rules
     */
    public function validate(array $data, array $rules): array
    {
        foreach ($rules as $field => $fieldRules) {
            $value = $data[$field] ?? null;
            $rulesList = explode('|', $fieldRules);

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

                $method = 'validate' . ucfirst($rule);
                if (method_exists($this, $method)) {
                    $this->$method($field, $value, $params, $data);
                }
            }
        }

        return [
            'valid' => empty($this->errors),
            'errors' => $this->errors
        ];
    }

    private function addError(string $field, string $message): void
    {
        $this->errors[$field][] = $message;
    }

    private function validateRequired(string $field, mixed $value): void
    {
        if ($value === null || (is_string($value) && trim($value) === '')) {
            $this->addError($field, "The $field field is required.");
        }
    }

    private function validateEmail(string $field, mixed $value): void
    {
        if ($value && !filter_var($value, FILTER_VALIDATE_EMAIL)) {
            $this->addError($field, "The $field must be a valid email address.");
        }
    }

    private function validateMin(string $field, mixed $value, array $params): void
    {
        $min = (int) $params[0];
        if (is_string($value) && strlen($value) < $min) {
            $this->addError($field, "The $field must be at least $min characters.");
        } elseif (is_numeric($value) && $value < $min) {
            $this->addError($field, "The $field must be at least $min.");
        }
    }

    private function validateMax(string $field, mixed $value, array $params): void
    {
        $max = (int) $params[0];
        if (is_string($value) && strlen($value) > $max) {
            $this->addError($field, "The $field may not be greater than $max characters.");
        } elseif (is_numeric($value) && $value > $max) {
            $this->addError($field, "The $field may not be greater than $max.");
        }
    }

    private function validateNumeric(string $field, mixed $value): void
    {
        if ($value && !is_numeric($value)) {
            $this->addError($field, "The $field must be a number.");
        }
    }

    private function validateRegex(string $field, mixed $value, array $params): void
    {
        $pattern = $params[0];
        if ($value && !preg_match($pattern, (string) $value)) {
            $this->addError($field, "The $field format is invalid.");
        }
    }

    private function validateUnique_db(string $field, mixed $value, array $params): void
    {
        if (!$value)
            return;
        $table = $params[0];
        $column = $params[1] ?? $field;
        $tenantId = $params[2] ?? null;

        $db = Database::getInstance();
        $sql = "SELECT COUNT(*) FROM $table WHERE $column = :val";
        $binds = [':val' => $value];

        if ($tenantId) {
            $sql .= " AND tenant_id = :tenant_id";
            $binds[':tenant_id'] = $tenantId;
        }

        $stmt = $db->prepare($sql);
        $stmt->execute($binds);
        if ($stmt->fetchColumn() > 0) {
            $this->addError($field, "The $field has already been taken.");
        }
    }

    private function validateExists_db(string $field, mixed $value, array $params): void
    {
        if (!$value)
            return;
        $table = $params[0];
        $column = $params[1] ?? $field;

        $db = Database::getInstance();
        $stmt = $db->prepare("SELECT COUNT(*) FROM $table WHERE $column = :val");
        $stmt->execute([':val' => $value]);
        if ($stmt->fetchColumn() == 0) {
            $this->addError($field, "The selected $field is invalid.");
        }
    }
}
