<?php

namespace App\DAO;

use App\DAO\DAOResponse\DAOResponse;
use App\DAO\DAOResponse\DAOResponseInterface;
use App\Entities\Usuario\Usuario;
use App\Mappers\UsuarioMapper;
use App\Models\EnderecoModel;
use App\Models\PacienteModel;
use App\Models\PessoaFisicaModel;
use App\Models\PessoaModel;
use App\Models\UsuarioModel;
use App\Services\GaleriaService;
use App\Services\LogService;
use App\Services\ServiceResponse\ServiceResponseInterface;
use App\Utils\Collections\QueryFilterCollection;
use App\ViewModels\Usuario\UsuarioDetailsViewModel\UsuarioDetailsViewModel;
use App\ViewModels\Usuario\UsuarioUpdateViewModel\UsuarioUpdateViewModel;
use CodeIgniter\HTTP\ResponseInterface;
use Exception;
use App\Services\JWT\UsuarioLogado;

class UsuarioDAO extends DAO {

	public function getUsuario(QueryFilterCollection $query_filter_collection): DAOResponse {

		$sql = '
            SELECT 
                id,
                email,
                senha,
                ativo,
                foto,
                email_verificado_em,
                criado_em,
                alterado_em,
                id_pessoa,
                adicionado_por,
                alterado_por
            FROM usuario
        ';
		$binds = [ ];
		return $this->getDAOResponseFromUniqueRowSelect( $binds, $query_filter_collection, $sql,
			function (array $result_array) {
				return UsuarioMapper::getUsuarioFromArray( $result_array [0] );
			} );

	}

	public function getUsuarioByEmail(string $email): DAOResponse {

		$sql = '
            SELECT 
                id,
                email,
                senha,
                ativo,
                foto,
                email_verificado_em,
                criado_em,
                alterado_em,
                id_pessoa,
                adicionado_por,
                alterado_por,
                id_chamefacil
            FROM 
                usuario 
            WHERE 
                email = :email:
        ';

		$binds = [ 
			"email" => $email
		];
		$query_filter_collection = new QueryFilterCollection();
		return $this->getDAOResponseFromUniqueRowSelect( $binds, $query_filter_collection, $sql,
			function (array $result_array) {
				return UsuarioMapper::getUsuarioFromArray( $result_array [0] );
			} );

	}

	public function updateUsuarioPassword(Usuario $usuario): DAOResponse {

		$sql = "UPDATE 
                    usuario
                SET 
                    senha = :senha:
                WHERE id = :id_usuario:
        ";
		$binds = [ 
			'senha' => $usuario->getSenha(),
			'id_usuario' => $usuario->getId()
		];
		$query = $this->db->query( $sql, $binds );
		$error = $this->db->error();
		$affected_rows = $query->connID->affected_rows;
		$this->db->close();
		$dao_response = new DAOResponse();
		if ($error && $error ['code']) {
			$status_response = $error ['code'];
			$message_log = "Ocorreu um erro durante a atualização da senha do usuário {$usuario->getId()}";
			$data_log = [ 
				'sql' => $sql,
				'binds' => $binds,
				'usuario' => $usuario->toArray()
			];
			(new LogService())->log_error( $status_response, $message_log, $data_log );
		} else {
			$status_response = $affected_rows === 1 ? DAOResponseInterface::SUCESS : DAOResponseInterface::DATABASE_CONNECTION_ERROR;
		}
		$dao_response->setStatus( $status_response );
		return $dao_response;

	}

	public function changePassword(int $id_usuario, string $current_passwd, string $new_passwd) {

		$sql = "UPDATE 
                    usuario
                SET 
                    senha = :senha:
                WHERE id = :id_usuario: AND senha = :senha_antiga:
        ";
		$binds = [ 
			'senha' => md5( $new_passwd ),
			'id_usuario' => $id_usuario,
			'senha_antiga' => md5( $current_passwd )
		];
		$query = $this->db->query( $sql, $binds );
		$error = $this->db->error();
		$affected_rows = $query->connID->affected_rows;
		$dao_response = new DAOResponse();
		if ($error && $error ['code']) {
			$status_response = $error ['code'];
		} else {
			$status_response = $affected_rows === 1 ? DAOResponseInterface::SUCESS : DAOResponseInterface::DATABASE_CONNECTION_ERROR;
		}

		$dao_response->setStatus( $status_response );
		return $dao_response;

	}

	public function persistUsuarioUpdateVM(int $id_usuario, UsuarioUpdateViewModel $usuarioUpdateViewModel): DAOResponse {

		$dao_response = new DAOResponse();

		if ($_FILES ['foto'] ['size'] != 0) {
			$usuarioUpdateViewModel->setFoto( $this->updateUsuarioFoto( $usuarioUpdateViewModel, $_POST ["usuario_antigo"] ) );
		} else {
			$usuarioUpdateViewModel->setFoto( $_POST ["usuario_antigo"] ["foto"] );
		}

		$this->db->transStart();
		$status_persistence = $this->persistEnderecoFromUsuarioUpdateVM( $id_usuario, $usuarioUpdateViewModel );
		$status_persistence = $status_persistence && $this->persistPessoaFromUsuarioUpdateVM( $id_usuario, $usuarioUpdateViewModel );
		$status_persistence = $status_persistence && $this->persistPessoaFisicaFromUsuarioUpdateVM( $id_usuario, $usuarioUpdateViewModel );
		$error = $this->db->error();
		if ($error && $error ['code']) {
			$this->setDatabaseErrorsForUsuarioUpdate( $dao_response, $error );
			return $dao_response;
		} else if ($status_persistence) {
			$this->db->transComplete();
			$dao_response->setStatus( DAOResponseInterface::SUCESS );
		} else {
			$dao_response->setStatus( DAOResponseInterface::INTERNAL_DAO_ERROR );
		}
		$this->db->close();

		return $dao_response;

	}

	private function updateUsuarioFoto(UsuarioUpdateViewModel $usuarioUpdateViewModel, array $usuario_antigo) {

		$target_dir = '../writable/perfil/';
		$extension = pathinfo( $_FILES ['foto'] ['name'], PATHINFO_EXTENSION );
		$file_dir = $target_dir . uniqid() . '.' . $extension;
		if (count( $_FILES ) > 0) {
			try {
				$sql = "UPDATE usuario SET foto=? WHERE id=?";
				$binds = [ 
					$file_dir,
					$usuario_antigo ['id']
				];
				file_put_contents( $file_dir, $usuarioUpdateViewModel->getFoto() );
				$query = $this->db->query( $sql, $binds );
				if ($usuario_antigo ["foto"]) {
					@unlink( $usuario_antigo ["foto"] );
				}
			} catch ( Exception $e ) {
				return (json_encode( $e->getMessage() ));
			}
		} else {
			$file_dir = $usuario_antigo ["foto"];
		}

		return $file_dir;

	}

	private function setDatabaseErrorsForUsuarioUpdate(DAOResponse $dao_response, array $error): void {

		$dao_response->setStatus( $error ['code'] );
		$dao_response->setErrors( $this->getDatabaseResponseErrorsForPersistence( $error ) );

	}

	private function persistEnderecoFromUsuarioUpdateVM(int $id_usuario, UsuarioUpdateViewModel $usuarioUpdateViewModel): bool {

		$endereco_model = new EnderecoModel();
		$endereco = UsuarioMapper::getEnderecoFromUsuarioUpdateVM( $usuarioUpdateViewModel );
		$sucess_update = $endereco_model->updateEnderecoByUsuarioID( $id_usuario, $endereco );
		return $sucess_update;

	}

	private function persistPessoaFromUsuarioUpdateVM(int $id_usuario, UsuarioUpdateViewModel $usuarioUpdateViewModel): bool {

		$pessoa_model = new PessoaModel();
		$pessoa = UsuarioMapper::getPessoaFromUsuarioUpdateVM( $usuarioUpdateViewModel );
		$sucess_update = $pessoa_model->updatePessoaByUsuarioID( $id_usuario, $pessoa );
		return $sucess_update;

	}

	private function persistPessoaFisicaFromUsuarioUpdateVM(int $id_usuario, UsuarioUpdateViewModel $usuarioUpdateViewModel): bool {

		$pessoa_fisica_model = new PessoaFisicaModel();
		$pessoa_fisica = UsuarioMapper::getPessoaFisicaFromUsuarioUpdateVM( $usuarioUpdateViewModel );
		$sucess_update = $pessoa_fisica_model->updatePessoaFisicaByUsuarioID( $id_usuario, $pessoa_fisica );
		return $sucess_update;

	}

	private function getDatabaseResponseErrorsForPersistence(array $error): array {

		$dao_response_errors = [ ];
		if ($error && $error ['code']) {
			if ($error ['code'] === DAOResponseInterface::FOREIGN_KEY_DONT_EXISTS) {
				if (strpos( $error ['message'], 'cep' ) !== false) {
					$dao_response_errors ['cep'] = 'Esse cep não existe.';
				}
			} else if ($error ['code'] === DAOResponseInterface::DUPLICATED_UNIQUE_KEY) {
				if (strpos( $error ['message'], '\'pessoa_fisica.cpf\'' ) !== false) {
					$dao_response_errors ['cpf'] = 'Uma pessoa com esse CPF já está cadastrada no sistema.';
				}
				if (strpos( $error ['message'], '\'pessoa_fisica.rg\'' ) !== false) {
					$dao_response_errors ['rg'] = 'Uma pessoa com esse RG já está cadastrada no sistema.';
				}
			}
		}
		return $dao_response_errors;

	}

	public function getUsuarioDadosFromUsuarioID(int $id_usuario): DAOResponse {

		$sql = <<<SQL
		    SELECT
		        pessoa_fisica.nome AS nome,
		        pessoa.telefone_1 AS telefone_1,
		        pessoa.telefone_2 AS telefone_2,
		        usuario.foto AS foto,
		        pessoa_fisica.cpf AS cpf,
		        pessoa_fisica.rg AS rg,
		        SUBSTRING(pessoa_fisica.data_nascimento, 1, 10) AS data_nascimento,
		        pessoa_fisica.genero AS genero,
		        cep.cep AS cep,
		        cep.logradouro AS logradouro,
		        cep.bairro AS bairro,
		        endereco.numero AS numero,
		        endereco.complemento AS complemento,
		        endereco.referencias AS referencias,
		        cep.cidade AS cidade,
		        cep.estado AS estado,
		        coalesce(cliente.id, 0) as cliente_id,
		        coalesce(medico.id, 0) as medico_id,
		        coalesce(assistente.id, 0) as assistente_id,
		        coalesce(paciente.id, 0) as paciente_id
		    from pessoa
		    inner join endereco on `pessoa`.`id_endereco` = `endereco`.id 
		    inner join usuario on `usuario`.`id_pessoa` = `pessoa`.id 
		    inner join pessoa_fisica on `pessoa_fisica`.`id_pessoa` = `pessoa`.id
		    inner join cep on `endereco`.cep = `cep`.id 
		    left join cliente on cliente.id_pessoa = pessoa.id
		    left join profissional on profissional.id_pessoa = pessoa.id 
		    left join medico on medico.id_profissional = profissional.id
		    left join assistente on assistente.id_profissional = profissional.id
		    left join paciente on paciente.id_pessoa = pessoa.id
		    where
		        `usuario`.id = :id_usuario:
		SQL;
		$binds = [ 
			'id_usuario' => $id_usuario
		];
		$query_filter_collection = new QueryFilterCollection();
		return $this->getDAOResponseFromUniqueRowSelect( $binds, $query_filter_collection, $sql,
			function (array $result_array) use ($id_usuario): UsuarioDetailsViewModel {
				$permissoes = $this->getPermissaoListByUsuarioId( $id_usuario );
				return UsuarioMapper::getUsuarioDetailsVMFromArray( $result_array [0], $permissoes );
			} );

	}

	public function getUsuarioDadosFromUsuarioIDRaw(int $id_usuario): DAOResponse {

		$sql = <<<SQL
		    SELECT
		        usuario.id AS usuario_id,
		        pessoa_fisica.nome AS nome,
		        pessoa.telefone_1 AS telefone_1,
		        pessoa.telefone_2 AS telefone_2,
		        usuario.foto AS foto,
		        pessoa_fisica.cpf AS cpf,
		        pessoa_fisica.rg AS rg,
		        SUBSTRING(pessoa_fisica.data_nascimento, 1, 10) AS data_nascimento,
		        pessoa_fisica.genero AS genero,
		        cep.cep AS cep,
		        cep.logradouro AS logradouro,
		        cep.bairro AS bairro,
		        endereco.numero AS numero,
		        endereco.complemento AS complemento,
		        endereco.referencias AS referencias,
		        cep.cidade AS cidade,
		        cep.estado AS estado
		    FROM `pessoa`, `endereco`, `pessoa_fisica`, `usuario`, `cep`
		    WHERE 
		          `pessoa`.`id_endereco` = `endereco`.id AND
		          `usuario`.`id_pessoa` = `pessoa`.id AND
		          `pessoa_fisica`.`id_pessoa` = `pessoa`.id AND
		          `usuario`.`id_pessoa` = `pessoa`.id AND
		          `endereco`.cep = `cep`.id AND
		          `usuario`.id = :id_usuario:
		SQL;
		$binds = [ 
			'id_usuario' => $id_usuario
		];
		$query_filter_collection = new QueryFilterCollection();
		return $this->getDAOResponseFromUniqueRowSelect( $binds, $query_filter_collection, $sql,
			function (array $result_array) use ($id_usuario): UsuarioDetailsViewModel {
				$permissoes = $this->getPermissaoListByUsuarioId( $id_usuario );
				return UsuarioMapper::getUsuarioDetailsVMFromArrayRaw( $result_array [0], $permissoes );
			} );

	}

	public function getPermissaoListByUsuarioId(int $usuarioId): array {

		$sql = <<<SQL
		    SELECT
		        p.id_endpoint as endpoint_id
		    FROM permissao p
		    INNER JOIN usuario_perfil up ON p.id_perfil = up.id_perfil
		    WHERE
		        up.id_usuario = $usuarioId
		        AND p.ativo
		    GROUP BY endpoint_id
		SQL;
		$result = $this->db->query( $sql )
			->getResultArray();
		$permissoes = [ ];
		foreach ( $result as $row ) :
			$permissoes [] = ( int ) ($row ['endpoint_id'] ?? 0);
		endforeach
		;
		return $permissoes;

	}

	public function getIdChameFacilFromUsuario(int $id_usuario) {

		$sql = '
            SELECT id_chamefacil
            FROM usuario
            WHERE usuario.id = :id_usuario:
        ';
		$binds = [ 
			'id_usuario' => $id_usuario
		];
		$query = $this->db->query( $sql, $binds );
		$result_array = $query->getResultArray();
		return ($result_array);

	}

	public function getPerfilUsuario(int $id_usuario) {

		$sql = '
            select up.id_perfil from usuario u inner join usuario_perfil up on u.id = up.id_usuario
            WHERE u.id = :id_usuario:
        ';
		$binds = [ 
			'id_usuario' => $id_usuario
		];
		$query = $this->db->query( $sql, $binds );
		$result_array = $query->getResultArray();
		return ($result_array);

	}

	public function getminhasUnidades(): DAOResponse {

		$sql = <<<SQL
			select
				u.id as id_usuario, un.id as id_unidade, un.descricao as unidade
			from 
				usuario u, profissional p, profissional_clinica pc , unidade un
			where
				u.id = :usuario_id: and un.ativo = 1
				and u.id_pessoa = p.id_pessoa
				and p.id = pc.id_profissional
				and pc.id_pessoa_juridica = un.id_pessoa_juridica
		SQL;

		$binds = [ 
			'usuario_id' => UsuarioLogado::getUsuarioId()
		];

		$dao_response = new DAOResponse();
		$conn = db_connect();
		$result = $conn->query( $sql, $binds )
			->getResultArray();
		$errors = $conn->error();

		if ($errors ['code'] != 0) {
			$dao_response->setStatus( DAOResponseInterface::DATABASE_CONNECTION_ERROR );
			$dao_response->setErrors( $errors );
		} else {
			if (count( $result ) > 0) {
				$dao_response->setStatus( DAOResponseInterface::SUCESS );
				$dao_response->setData( $result );
			} else {
				$dao_response->setStatus( DAOResponseInterface::RECORD_NOT_FOUND );
				$dao_response->setData( [ ] );
			}
		}

		return $dao_response;

	}

	public function getDadosUsuarioByIdToLojaCompra(int $id_usuario): DAOResponse {

		$sql = <<<SQL
			SELECT DISTINCT
				u.email , pf.nome , pf.cpf, p.telefone_1, c.logradouro , e.numero,
				e.complemento ,c.bairro , c.cidade, c.estado, c.cep
			FROM
				usuario u, pessoa_fisica pf , pessoa p, endereco e , cep c
			WHERE
				u.id = :usuario_id:
				and u.id_pessoa = pf.id_pessoa
				and u.id_pessoa = p.id
				and p.id_endereco = e.id
				and e.cep = c.id
			LIMIT 0,1
		SQL;

		$binds = [ 
			'usuario_id' => $id_usuario
		];

		$dao_response = new DAOResponse();
		$conn = db_connect();
		$result = $conn->query( $sql, $binds )
			->getResultArray();
		$errors = $conn->error();

		if ($errors ['code'] != 0) {
			$dao_response->setStatus( DAOResponseInterface::DATABASE_CONNECTION_ERROR );
			$dao_response->setErrors( $errors );
		} else {
			if (count( $result ) > 0) {
				$dao_response->setStatus( DAOResponseInterface::SUCESS );
				$dao_response->setData( $result );
			} else {
				$dao_response->setStatus( DAOResponseInterface::RECORD_NOT_FOUND );
				$dao_response->setData( [ ] );
			}
		}

		return $dao_response;

	}

}
