package sac.pessoa.fisica.jdbc;


import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.ArrayList;

import sac.exception.InvalidArgumentException;
import sac.exception.ItemNaoCadastradoException;
import sac.exception.NullArgumentException;
import sac.doenca.DBDoenca;
import sac.doenca.RepositorioDoenca;
import sac.persistencia.OID;
import sac.persistencia.PersistenceException;
import sac.pessoa.SenhaInvalidaException;
import sac.pessoa.endereco.RepositorioEndereco;
import sac.pessoa.endereco.Endereco;
import sac.pessoa.endereco.jdbc.DBEndereco;
import sac.pessoa.fisica.CPFInvalidoException;
import sac.pessoa.fisica.PessoaFisica;
import sac.pessoa.fisica.RepositorioTipoServico;
import sac.pessoa.util.jdbc.PersistenteFactory;

public class PersistenteFactory_Person implements PersistenteFactory{
    private static PersistenteFactory_Person instancia;

    private RepositorioEndereco repositorioEndereco;
    private RepositorioDoenca repositorioDoenca;
    private RepositorioTipoServico repositorioServico;

    private final String preparedSelectCodetDeceaseSql =
            "select cd_doenca from rel_pf_doenca where cd_pf = ?";
   private final String preparedSelectCodetTypeServiceSql =
            "select cd_tipo_servico from rel_pf_servico where cd_pf = ?";



    public PersistenteFactory_Person ()  throws PersistenceException {
        repositorioEndereco = DBEndereco.getInstancia();
        repositorioDoenca = DBDoenca.getInstancia();
        repositorioServico =  DBTipoServico.getInstancia();
    }

    public static synchronized PersistenteFactory_Person getInstancia() throws PersistenceException {
        if (instancia == null) {
            instancia = new PersistenteFactory_Person();
        }
        return instancia;
    }


    public Object produce (Connection con, ResultSet rs) throws PersistenceException{
        try {
            PessoaFisica pessoa;
            if (rs.next()){

                long id = rs.getLong("cd_pf");
                String cpf = rs.getString("tx_cpf");
                String nome = rs.getString("nm_pf");
                String senha = rs.getString("tx_senha");
                String descricao = rs.getString("ds_atividades");
                char status = rs.getString("ch_identificador").charAt(0);
                long codigoEndereco = rs.getLong("cd_endereco");

                Endereco endereco;
                try {
                    endereco = repositorioEndereco.procurar( new OID (codigoEndereco));
                } catch ( ItemNaoCadastradoException iteme) {
                    throw new PersistenceException ("Inconsistencia no Banco. Endereco inxistente", iteme );
                }

                pessoa = new PessoaFisica(nome,cpf,senha, endereco, consultarDoenca(id, con));
                pessoa.setId(new OID (id));
                pessoa.setStatus(status);
                pessoa.setDescricaoAtividades(descricao);
                pessoa.setTiposServicos(consultarTipoServico(id, con));

            } else {
                pessoa = null;
            }
            rs.close();
            return pessoa;
        } catch (NullArgumentException nulle) {
            throw new PersistenceException("Nao foi possivel criar Pessoa Fisica", nulle);
        } catch (SenhaInvalidaException senhae) {
            throw new PersistenceException("Senha contida no banco e invalida", senhae);
        } catch (CPFInvalidoException cpfe) {
            throw new PersistenceException("CPF contido no banco e invalido", cpfe);
        } catch (InvalidArgumentException ine) {
            throw new PersistenceException("Nao foi possivel criar Pessoa Fisica", ine);
        } catch (SQLException ex) {
            throw new PersistenceException("Ocorreu um erro no Banco", ex);
        }

    }

   /**
   * Método para consulta de downcas da pessoa física no BD.
   *
   * @param codigo    long contendo ID a ser procurado no BD.
   * @param con       Connection já estabelecida na sua função pai.
   *
   * @return List     Lista correspodente as doenças encontrados na busca.
   */
    private List consultarDoenca(long codigoPessoa, Connection con) throws PersistenceException{
        List doencas = new ArrayList();
        try{
            // prepara consulta pelos codigos
            PreparedStatement preCodigos = con.prepareStatement(preparedSelectCodetDeceaseSql);
            preCodigos.setLong(1,codigoPessoa);
            ResultSet rs = preCodigos.executeQuery();
            try {
                while (rs.next()){
                long id_doenca = rs.getLong("cd_doenca");
                doencas.add( repositorioDoenca.procurar(new OID (id_doenca) ));
                }
            } catch ( ItemNaoCadastradoException iteme){
                throw new PersistenceException ("Problemas no BD.", iteme);
            } finally {
                rs.close();
                preCodigos.close();
            }
        } catch (SQLException sqle){
            throw new PersistenceException ("Problemas no BD.", sqle);
        }
        return doencas;
   }

   /**
   * Método para consulta de tipos de serviços da pessoa física no BD.
   *
   * @param codigo    long contendo ID a ser procurado no BD.
   * @param con       Connection já estabelecida na sua função pai.
   *
   * @return List     Lista correspodente as doenças encontrados na busca.
   */
    private List consultarTipoServico(long codigoPessoa, Connection con) throws PersistenceException{
        List servicos = new ArrayList();
        try{
            // prepara consulta pelos codigos
            PreparedStatement preCodigos = con.prepareStatement(preparedSelectCodetTypeServiceSql);
            preCodigos.setLong(1,codigoPessoa);
            ResultSet rs = preCodigos.executeQuery();
            try {
                while (rs.next()){
                    long id_servico = rs.getLong("cd_tipo_servico");
                    servicos.add( repositorioServico.procurar(new OID (id_servico) ));
                }
            } catch ( ItemNaoCadastradoException iteme){
                throw new PersistenceException ("Problemas no BD.", iteme);
            } finally {
                rs.close();
                preCodigos.close();
            }
        } catch (SQLException sqle){
            throw new PersistenceException ("Problemas no BD.", sqle);
        }
        return servicos;
   }
}