<?php
/**
 * Arquivo de declaração da classe UsuarioDAO.
 */
class UsuarioDAO extends Model
{
    private $conexao;

    /**
     * Método construtor.
     */
    public function __construct()
    {
        $this->conexao = Conexao::getInstancia();
    }

    /**
     * Método para retornar a quantidade de usuarios, conforme condição informada.
     *
     * @param  string $condicao
     * @param  array  $parametros
     * @return string
     */
    public function contador($condicao = '', $parametros = array())
    {
        $condicao = Util::verificaCampo($condicao, 'AND');

        $txtSql = "SELECT count(id) as quantidade FROM cp_usuarios WHERE ((excluido IS NULL) OR (excluido = 0))  $condicao";

        try {
            $sql = $this->conexao->getConexao()->prepare($txtSql);
            $this->conexao->setParametros($sql, $parametros);
            $sql->execute();
        } catch (PDOException $ex) {
            throw new BancoExcecao($ex->getMessage(), $txtSql, __FILE__, __CLASS__, __METHOD__, __LINE__);
        }

        $resultado = $sql->fetchColumn();
        $sql->closeCursor();

        return (int) $resultado;
    }

    /**
     * Método para retornar o ultimo id.
     *
     * @return string
     */
    public function ultimoId()
    {
        $txtSql = 'SELECT max(id) as max FROM cp_usuarios';

        try {
            $sql = $this->conexao->getConexao()->prepare($txtSql);
            $sql->execute();
        } catch (PDOException $ex) {
            throw new BancoExcecao($ex->getMessage(), $txtSql, __FILE__, __CLASS__, __METHOD__, __LINE__);
        }

        $resultado = $sql->fetchColumn();
        $sql->closeCursor();

        return (int) $resultado;
    }

    /**
     * Método para inserir.
     *
     * @param  Usuario $obj
     */
    public function inserir(Usuario $obj)
    {
        $hash = new PasswordHash(8, false);
        $senha = $hash->hashPassword($obj->senha);

        if ($obj->login) {
            $login = self::geraLogin($obj->login);
        } else {
            $login = self::geraLogin($obj->nome);
        }

        $txtSql = 'INSERT INTO cp_usuarios
                        (perfil_id, nome, email, login, senha)
                    VALUES
                        (:perfil, :nome, :email, :login, :senha)';

        try {
            $sql = $this->conexao->getConexao()->prepare($txtSql);
            $sql->bindValue(':perfil', $obj->perfil, PDO::PARAM_INT);
            $sql->bindValue(':nome', $obj->nome, PDO::PARAM_STR);
            $sql->bindValue(':email', $obj->email, PDO::PARAM_STR);
            $sql->bindValue(':login', $login, PDO::PARAM_STR);
            $sql->bindValue(':senha', $senha, PDO::PARAM_STR);
            $sql->execute();
        } catch (PDOException $ex) {
            throw new BancoExcecao($ex->getMessage(), $txtSql, __FILE__, __CLASS__, __METHOD__, __LINE__);
        }

        $lastId = $sql->rowCount() > 0 ? (int) $this->conexao->getConexao()->lastInsertId() : null;
        $sql->closeCursor();

        return $lastId;
    }

    /**
     * Editar dados.
     *
     * @param  Usuario $obj
     */
    public function editar(Usuario $obj)
    {
        $txtSqlPerfil = '';
        $txtSqlSenha = '';

        if ('' != $obj->senha) {
            $hash = new PasswordHash(8, false);
            $senha = $hash->hashPassword($obj->senha);

            $txtSqlSenha = ', senha = :senha';
        }

        if ('' != $obj->perfil) {
            $txtSqlPerfil = ', perfil_id = :perfil';
        }

        if ($obj->login) {
            $login = self::geraLogin($obj->login, $obj->id);
        } else {
            $login = self::geraLogin($obj->nome, $obj->id);
        }

        $txtSql = 'UPDATE cp_usuarios SET
                        nome = :nome,
                        email = :email,
                        login = :login,
                        atualizado_em = NOW()
                        ' . $txtSqlPerfil . $txtSqlSenha . '
                        WHERE id = :id';

        try {
            $sql = $this->conexao->getConexao()->prepare($txtSql);
            $sql->bindValue(':nome', $obj->nome, PDO::PARAM_STR);
            $sql->bindValue(':email', $obj->email, PDO::PARAM_STR);
            $sql->bindValue(':login', $login, PDO::PARAM_STR);

            if ('' != $txtSqlPerfil) {
                $sql->bindValue(':perfil', $obj->perfil, PDO::PARAM_INT);
            }

            if ('' != $txtSqlSenha) {
                $sql->bindValue(':senha', $senha, PDO::PARAM_STR);
            }

            $sql->bindValue(':id', $obj->id, PDO::PARAM_INT);
            $sql->execute();
        } catch (PDOException $ex) {
            throw new BancoExcecao($ex->getMessage(), $txtSql, __FILE__, __CLASS__, __METHOD__, __LINE__);
        }

        $sql->closeCursor();
    }

    /**
     * Método para excluir.
     *
     * @param  int $id
     */
    public function excluir($id = 0)
    {
        $txtSql = 'UPDATE cp_usuarios SET excluido = now() WHERE id = :id AND id <> :meuId';

        try {
            $sql = $this->conexao->getConexao()->prepare($txtSql);
            $sql->bindValue(':meuId', $_SESSION['sesCMSCodigo'], PDO::PARAM_INT);
            $sql->bindValue(':id', $id, PDO::PARAM_INT);
            $sql->execute();
        } catch (PDOException $ex) {
            throw new BancoExcecao($ex->getMessage(), $txtSql, __FILE__, __CLASS__, __METHOD__, __LINE__);
        }

        $resultado = $sql->rowCount() > 0 ? true : false;
        $sql->closeCursor();

        return $resultado;
    }

    /**
     * Método para listar os objetos.
     *
     * @param  string $condicao
     * @param  array  $parametros
     * @param  string $order
     * @param  string $limit
     * @param  string $offset
     */
    public function listar($condicao = '', $parametros = array(), $order = '', $limit = '', $offset = '')
    {
        $condicao = Util::verificaCampo($condicao, 'AND');
        $order = Util::verificaCampo($order, 'ORDER BY');
        $limit = Util::verificaCampo($limit, 'LIMIT');
        $offset = Util::verificaCampo($offset, 'OFFSET');

        $txtSql = "SELECT
                        cp_usuarios.id as id,
                        cp_usuarios.perfil_id as perfil_id,
                        cp_usuarios.nome as nome,
                        cp_usuarios.email as email,
                        cp_usuarios.login as login,
                        cp_perfil.nome as perfilNome,
                        cp_perfil.ativo as ativo,
                        cp_perfil.criado_em as datacriacao
                    FROM cp_usuarios
                    INNER JOIN cp_perfil ON cp_perfil.id = cp_usuarios.perfil_id
                    WHERE ((cp_usuarios.excluido IS NULL) OR (cp_usuarios.excluido = 0)) $condicao $order $limit $offset";

        try {
            $sql = $this->conexao->getConexao()->prepare($txtSql);
            $this->conexao->setParametros($sql, $parametros);
            $sql->execute();
        } catch (PDOException $ex) {
            throw new BancoExcecao($ex->getMessage(), $txtSql, __FILE__, __CLASS__, __METHOD__, __LINE__);
        }

        $resultado = $sql->fetchAll(PDO::FETCH_ASSOC);
        $sql->closeCursor();

        if (!$resultado) {
            return null;
        }

        foreach ($resultado as $res) {
            $objPerfil = new Perfil();
            $objPerfil->id = $res['perfil_id'];
            $objPerfil->nome = $res['perfilNome'];

            $arrObjeto[] = $this->classObject($res, $objPerfil);
        }

        return $arrObjeto;
    }

    /**
     * Método que valida o login.
     *
     * @param  string $email
     * @param  string $senha
     */
    public function login($email = '', $senha = '')
    {
        $txtSql = 'SELECT
                        cp_usuarios.id as id,
                        cp_usuarios.perfil_id as perfil_id,
                        cp_usuarios.nome as nome,
                        cp_usuarios.email as email,
                        cp_usuarios.senha as senha,
                        cp_usuarios.login as login
                    FROM cp_usuarios
                    INNER JOIN cp_perfil ON cp_perfil.id = cp_usuarios.perfil_id
                    WHERE (cp_usuarios.email = :email OR cp_usuarios.login = :email)
                    AND cp_usuarios.ativo = 1
                    AND ((cp_usuarios.excluido IS NULL) OR (cp_usuarios.excluido = 0)) 
                    AND cp_perfil.ativo = 1
                    LIMIT 1';

        try {
            $sql = $this->conexao->getConexao()->prepare($txtSql);
            $sql->bindValue(':email', $email, PDO::PARAM_STR);
            $sql->execute();
        } catch (PDOException $ex) {
            throw new BancoExcecao($ex->getMessage(), $txtSql, __FILE__, __CLASS__, __METHOD__, __LINE__);
        }

        $resultado = $sql->fetch(PDO::FETCH_ASSOC);
        //print_r($resultado);die();
        $sql->closeCursor();

        if (!$resultado) {
            return null;
        }

        $hash = new PasswordHash(8, false);

        if (!$hash->checkPassword($senha, $resultado['senha'])) {
            return null;
        }

        return $this->classObject($resultado);
    }

    /**
     * Método para buscar.
     *
     * @param  int $id
     * @return object
     */
    public function buscar($id = 0)
    {
        $select = $this->getSelect();

        $txtSql = "SELECT $select FROM cp_usuarios WHERE id = :id AND ((excluido IS NULL) OR (excluido = 0)) ";

        try {
            $sql = $this->conexao->getConexao()->prepare($txtSql);
            $sql->bindValue(':id', $id, PDO::PARAM_INT);
            $sql->execute();
        } catch (PDOException $ex) {
            throw new BancoExcecao($ex->getMessage(), $txtSql, __FILE__, __CLASS__, __METHOD__, __LINE__);
        }

        $resultado = $sql->fetch(PDO::FETCH_ASSOC);
        $sql->closeCursor();

        if (!$resultado) {
            return null;
        }

        return $this->classObject($resultado);
    }

    /**
     * Método para buscar pelo email.
     *
     * @param  string  $email
     * @param  int $id
     * @return object
     */
    public function buscarPeloEmail($email = '', $id = 0)
    {
        $where = array();
        $param = array();
        $param[] = Util::montaParametros(':email', $email, 'STR');

        $where[] = 'cp_usuarios.email = :email';

        if ($id > 0) {
            $param[] = Util::montaParametros(':id', $id, 'INT');
            $where[] = 'cp_usuarios.id NOT IN (:id)';
        }

        $where = implode(' AND ', $where);

        $objUsuario = self::listar($where, $param);

        return (count($objUsuario) > 0) ? $objUsuario[0] : null;
    }

    /**
     * Método para buscar pelo login.
     *
     * @param  string  $login
     * @param  int $id
     * @return object
     */
    public function buscarPeloLogin($login = '', $id = 0)
    {
        $where = array();
        $param = array();
        $param[] = Util::montaParametros(':login', $login, 'STR');

        $where[] = 'cp_usuarios.login = :login';

        if ($id > 0) {
            $param[] = Util::montaParametros(':id', $id, 'INT');
            $where[] = 'cp_usuarios.id NOT IN (:id)';
        }

        $where = implode(' AND ', $where);

        $objUsuario = self::listar($where, $param);

        return (count($objUsuario) > 0) ? $objUsuario[0] : null;
    }

    /**
     * Método para mudar o status.
     *
     * @param  int $id
     */
    public function mudaStatus($id = 0)
    {
        $txtSql = 'SELECT ativo FROM cp_usuarios WHERE id = :id;
                UPDATE cp_usuarios SET ativo = (ativo ^ 1), atualizado_em = NOW() WHERE id = :id';

        try {
            $sql = $this->conexao->getConexao()->prepare($txtSql);
            $sql->bindValue(':id', $id, PDO::PARAM_INT);
            $sql->execute();
        } catch (PDOException $ex) {
            throw new BancoExcecao($ex->getMessage(), $txtSql, __FILE__, __CLASS__, __METHOD__, __LINE__);
        }

        $resultado = $sql->fetchColumn();
        $sql->closeCursor();

        return (int) !$resultado;
    }

    /**
     * Editar token.
     *
     * @param  int $id
     * @param  string  $token
     */
    public function mudarToken($id = 0, $token = '')
    {
        $txtSql = 'UPDATE cp_usuarios SET token = :token, atualizado_em = NOW() WHERE id = :id';

        try {
            $sql = $this->conexao->getConexao()->prepare($txtSql);
            $sql->bindValue(':token', $token, PDO::PARAM_STR);
            $sql->bindValue(':id', $id, PDO::PARAM_INT);
            $sql->execute();
        } catch (PDOException $ex) {
            throw new BancoExcecao($ex->getMessage(), $txtSql, __FILE__, __CLASS__, __METHOD__, __LINE__);
        }

        $sql->closeCursor();
    }

    /**
     * Validar token.
     *
     * @param  string $token
     * @return object
     */
    public function validarToken($token = '')
    {
        $txtSql = 'SELECT id, nome, email FROM cp_usuarios WHERE token = :token';

        try {
            $sql = $this->conexao->getConexao()->prepare($txtSql);
            $sql->bindValue(':token', $token, PDO::PARAM_STR);
            $sql->execute();
        } catch (PDOException $ex) {
            throw new BancoExcecao($ex->getMessage(), $txtSql, __FILE__, __CLASS__, __METHOD__, __LINE__);
        }

        $resultado = $sql->fetch(PDO::FETCH_ASSOC);
        $sql->closeCursor();

        if (!$resultado) {
            return null;
        }

        return $this->classObject($resultado);
    }

    /**
     * Método para verificar se o login existe.
     *
     * @param  string  $login
     * @param  int $id
     * @return string
     */
    public function checaLogin($login = '', $id = 0)
    {
        $param = array();
        $where = '';

        $param[] = Util::montaParametros(':login', $login, 'STR');
        $where .= 'login = :login';

        if ($id > 0) {
            $param[] = Util::montaParametros(':id', $id, 'STR');
            $where .= ' AND id NOT IN (:id)';
        }

        $contador = self::contador($where, $param);

        return $contador;
    }

    /**
     * Método para gerar o login.
     *
     * @param  string  $login
     * @param  int $id
     * @return string
     */
    public function geraLogin($login = '', $id = 0)
    {
        $login = Util::slugify($login);

        do {
            $result = self::checaLogin($login, $id);

            $explode = explode('-', $login);
            $end = end($explode);

            $loginNumber = is_numeric($end) ? $end : $result;

            $login = ($result > 0) ? str_replace('-' . $loginNumber, '', $login) . '-' . ($loginNumber + 1) : $login;
        } while ($result > 0);

        return $login;
    }

    /**
     * Objeto da classe.
     *
     * @param  array  $resultado
     * @param null|mixed $objPerfil
     * @return object
     */
    private function classObject($resultado = array(), $objPerfil = null)
    {
        $obj = null;
        $obj = new Usuario();
        isset($resultado['id']) ? $obj->id = $resultado['id'] : '';
        isset($objPerfil) ? $obj->perfil = $objPerfil : (isset($resultado['perfil_id']) ? $obj->perfil = $resultado['perfil_id'] : '');
        isset($resultado['nome']) ? $obj->nome = $resultado['nome'] : '';
        isset($resultado['email']) ? $obj->email = $resultado['email'] : '';
        isset($resultado['login']) ? $obj->login = $resultado['login'] : '';
        isset($resultado['ativo']) ? $obj->ativo = $resultado['ativo'] : '';
        isset($resultado['criadoem']) ? $obj->criado_em = $resultado['criadoem'] : '';

        return $obj;
    }
}
