<?php
// ============================================================
// validador.php
// Classe centralizada para validação de dados
// Uso: $v = new Validador(); $v->texto($_POST['nome'], 3, 100);
// ============================================================

class Validador {
    private $erros = [];

    /**
     * Validar texto (string)
     * @param mixed $valor Valor a validar
     * @param int $min Comprimento mínimo
     * @param int $max Comprimento máximo
     * @param string $nome Nome do campo (para mensagem de erro)
     * @return string Valor validado
     */
    public function texto($valor, $min = 1, $max = 255, $nome = 'Campo') {
        if (empty($valor) && $min > 0) {
            throw new Exception("$nome é obrigatório");
        }
        $valor = trim((string)$valor);
        if (strlen($valor) < $min) {
            throw new Exception("$nome deve ter no mínimo $min caracteres");
        }
        if (strlen($valor) > $max) {
            throw new Exception("$nome deve ter no máximo $max caracteres");
        }
        return $valor;
    }

    /**
     * Validar número inteiro
     * @param mixed $valor Valor a validar
     * @param int $min Valor mínimo
     * @param int $max Valor máximo
     * @param string $nome Nome do campo
     * @return int Valor validado
     */
    public function inteiro($valor, $min = null, $max = null, $nome = 'Campo') {
        if (!is_numeric($valor)) {
            throw new Exception("$nome deve ser um número inteiro");
        }
        $valor = (int)$valor;
        if ($min !== null && $valor < $min) {
            throw new Exception("$nome deve ser no mínimo $min");
        }
        if ($max !== null && $valor > $max) {
            throw new Exception("$nome deve ser no máximo $max");
        }
        return $valor;
    }

    /**
     * Validar número decimal
     * @param mixed $valor Valor a validar
     * @param float $min Valor mínimo
     * @param float $max Valor máximo
     * @param string $nome Nome do campo
     * @return float Valor validado
     */
    public function decimal($valor, $min = null, $max = null, $nome = 'Campo') {
        if (!is_numeric($valor)) {
            throw new Exception("$nome deve ser um número");
        }
        $valor = (float)$valor;
        if ($min !== null && $valor < $min) {
            throw new Exception("$nome deve ser no mínimo $min");
        }
        if ($max !== null && $valor > $max) {
            throw new Exception("$nome deve ser no máximo $max");
        }
        return $valor;
    }

    /**
     * Validar email
     * @param string $valor Email a validar
     * @param string $nome Nome do campo
     * @return string Email validado
     */
    public function email($valor, $nome = 'Email') {
        $valor = trim((string)$valor);
        if (empty($valor)) {
            throw new Exception("$nome é obrigatório");
        }
        if (!filter_var($valor, FILTER_VALIDATE_EMAIL)) {
            throw new Exception("$nome inválido");
        }
        return strtolower($valor);
    }

    /**
     * Validar data (YYYY-MM-DD)
     * @param string $valor Data a validar
     * @param string $nome Nome do campo
     * @return string Data validada
     */
    public function data($valor, $nome = 'Data') {
        $valor = trim((string)$valor);
        if (empty($valor)) {
            throw new Exception("$nome é obrigatória");
        }
        if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $valor)) {
            throw new Exception("$nome deve estar no formato YYYY-MM-DD");
        }
        // Verificar se é uma data válida
        $partes = explode('-', $valor);
        if (!checkdate($partes[1], $partes[2], $partes[0])) {
            throw new Exception("$nome inválida");
        }
        return $valor;
    }

    /**
     * Validar enum (lista de valores permitidos)
     * @param mixed $valor Valor a validar
     * @param array $opcoes Valores permitidos
     * @param string $nome Nome do campo
     * @return string Valor validado
     */
    public function enum($valor, $opcoes = [], $nome = 'Campo') {
        $valor = (string)$valor;
        if (empty($valor)) {
            throw new Exception("$nome é obrigatório");
        }
        if (!in_array($valor, $opcoes, true)) {
            throw new Exception("$nome inválido. Valores permitidos: " . implode(', ', $opcoes));
        }
        return $valor;
    }

    /**
     * Validar booleano
     * @param mixed $valor Valor a validar
     * @param string $nome Nome do campo
     * @return bool Valor validado
     */
    public function booleano($valor, $nome = 'Campo') {
        if (is_bool($valor)) {
            return $valor;
        }
        if (in_array($valor, ['1', 'true', 'sim', 'yes', 'on', 1, true], true)) {
            return true;
        }
        if (in_array($valor, ['0', 'false', 'não', 'no', 'off', 0, false], true)) {
            return false;
        }
        throw new Exception("$nome deve ser verdadeiro ou falso");
    }

    /**
     * Validar URL
     * @param string $valor URL a validar
     * @param string $nome Nome do campo
     * @return string URL validada
     */
    public function url($valor, $nome = 'URL') {
        $valor = trim((string)$valor);
        if (empty($valor)) {
            throw new Exception("$nome é obrigatória");
        }
        if (!filter_var($valor, FILTER_VALIDATE_URL)) {
            throw new Exception("$nome inválida");
        }
        return $valor;
    }

    /**
     * Validar telefone (apenas números, 10-11 dígitos)
     * @param string $valor Telefone a validar
     * @param string $nome Nome do campo
     * @return string Telefone validado
     */
    public function telefone($valor, $nome = 'Telefone') {
        $valor = preg_replace('/\D/', '', (string)$valor);
        if (strlen($valor) < 10 || strlen($valor) > 11) {
            throw new Exception("$nome deve ter 10 ou 11 dígitos");
        }
        return $valor;
    }

    /**
     * Validar CPF (formato básico)
     * @param string $valor CPF a validar
     * @param string $nome Nome do campo
     * @return string CPF validado
     */
    public function cpf($valor, $nome = 'CPF') {
        $valor = preg_replace('/\D/', '', (string)$valor);
        if (strlen($valor) !== 11) {
            throw new Exception("$nome deve ter 11 dígitos");
        }
        // Verificar se todos os dígitos são iguais (CPF inválido)
        if (preg_match('/^(\d)\1{10}$/', $valor)) {
            throw new Exception("$nome inválido");
        }
        return $valor;
    }

    /**
     * Validar arquivo (verificar extensão e tamanho)
     * @param array $arquivo Array $_FILES['campo']
     * @param array $extensoes Extensões permitidas
     * @param int $tamanho_max Tamanho máximo em bytes
     * @param string $nome Nome do campo
     * @return array Arquivo validado
     */
    public function arquivo($arquivo, $extensoes = [], $tamanho_max = 5242880, $nome = 'Arquivo') {
        if (!isset($arquivo['tmp_name']) || empty($arquivo['tmp_name'])) {
            throw new Exception("$nome não foi enviado");
        }
        if ($arquivo['error'] !== UPLOAD_ERR_OK) {
            throw new Exception("Erro ao enviar $nome");
        }
        if ($arquivo['size'] > $tamanho_max) {
            throw new Exception("$nome deve ter no máximo " . ($tamanho_max / 1024 / 1024) . "MB");
        }
        $ext = strtolower(pathinfo($arquivo['name'], PATHINFO_EXTENSION));
        if (!empty($extensoes) && !in_array($ext, $extensoes)) {
            throw new Exception("$nome deve ser um dos tipos: " . implode(', ', $extensoes));
        }
        return $arquivo;
    }

    /**
     * Validar ID (inteiro positivo)
     * @param mixed $valor ID a validar
     * @param string $nome Nome do campo
     * @return int ID validado
     */
    public function id($valor, $nome = 'ID') {
        $valor = $this->inteiro($valor, 1, null, $nome);
        return $valor;
    }

    /**
     * Obter lista de erros
     * @return array Erros acumulados
     */
    public function erros() {
        return $this->erros;
    }

    /**
     * Limpar lista de erros
     */
    public function limpar_erros() {
        $this->erros = [];
    }

    /**
     * Adicionar erro manualmente
     * @param string $mensagem Mensagem de erro
     */
    public function adicionar_erro($mensagem) {
        $this->erros[] = $mensagem;
    }

    /**
     * Verificar se há erros
     * @return bool True se há erros
     */
    public function tem_erros() {
        return !empty($this->erros);
    }
}

?>
