import unittest
from unittest.mock import patch

from lib.conectividade import (
    DestinoDeRede,
    _separar_endereco_e_porta,
    destinos_do_servidor_linux,
    montar_comando_dns,
    montar_comando_ping,
    montar_comando_tcp,
    montar_mensagem_sem_rede,
    verificar_destinos,
)


class ExecutorFake:
    """Simula a execucao remota: devolve o codigo de retorno mapeado por trecho do comando."""

    def __init__(self, codigos_por_trecho=None, codigo_padrao=0):
        self.codigos_por_trecho = codigos_por_trecho or {}
        self.codigo_padrao = codigo_padrao
        self.comandos = []

    def __call__(self, comando):
        self.comandos.append(comando)

        for trecho, codigo in self.codigos_por_trecho.items():
            if trecho in comando:
                return codigo

        return self.codigo_padrao


DESTINO_REGISTRO = DestinoDeRede("Registro Docker", "gitlab.microuniverso.com.br", 5050, "imagens")
DESTINO_INTERNET = DestinoDeRede("Internet", "google.com", 443, "saida para a internet")


class TestSepararEnderecoEPorta(unittest.TestCase):
    def test_usa_a_porta_do_endereco(self):
        endereco, _, porta = _separar_endereco_e_porta("gitlab.microuniverso.com.br:5050")

        self.assertEqual(endereco, "gitlab.microuniverso.com.br")
        self.assertEqual(porta, 5050)

    def test_usa_porta_padrao_quando_nao_informada(self):
        endereco, _, porta = _separar_endereco_e_porta("gitlab.microuniverso.com.br")

        self.assertEqual(endereco, "gitlab.microuniverso.com.br")
        self.assertEqual(porta, 443)

    def test_ignora_porta_nao_numerica(self):
        _, _, porta = _separar_endereco_e_porta("gitlab.microuniverso.com.br:porta")

        self.assertEqual(porta, 443)


class TestComandos(unittest.TestCase):
    def test_comando_tcp_usa_dev_tcp_do_bash_com_timeout(self):
        comando = montar_comando_tcp("gitlab.microuniverso.com.br", 5050, timeout=8)

        self.assertIn("timeout 8", comando)
        self.assertIn("/dev/tcp/gitlab.microuniverso.com.br/5050", comando)

    def test_comando_dns_usa_getent(self):
        self.assertIn("getent hosts google.com", montar_comando_dns("google.com"))

    def test_comando_ping_nao_bloqueia_indefinidamente(self):
        comando = montar_comando_ping("google.com")

        self.assertIn("-c 1", comando)
        self.assertIn("-W 3", comando)


class TestVerificarDestinos(unittest.TestCase):
    def test_destino_acessivel_tem_dns_e_tcp_ok(self):
        executor = ExecutorFake()

        resultados = verificar_destinos(executor, [DESTINO_REGISTRO])

        self.assertTrue(resultados[0].dns_ok)
        self.assertTrue(resultados[0].tcp_ok)
        self.assertEqual(resultados[0].detalhe, "")

    def test_falha_de_dns_nao_tenta_tcp(self):
        executor = ExecutorFake({"getent hosts": 2})

        resultados = verificar_destinos(executor, [DESTINO_REGISTRO])

        self.assertFalse(resultados[0].dns_ok)
        self.assertFalse(resultados[0].tcp_ok)
        self.assertIn("DNS", resultados[0].detalhe)
        self.assertFalse(any("/dev/tcp" in comando for comando in executor.comandos))

    def test_porta_bloqueada_e_reportada_como_dns_ok_e_tcp_falho(self):
        executor = ExecutorFake({"/dev/tcp": 124})

        resultados = verificar_destinos(executor, [DESTINO_REGISTRO])

        self.assertTrue(resultados[0].dns_ok)
        self.assertFalse(resultados[0].tcp_ok)
        self.assertIn("5050", resultados[0].detalhe)

    def test_verifica_cada_destino_da_lista(self):
        executor = ExecutorFake({"google.com": 1})

        resultados = verificar_destinos(executor, [DESTINO_REGISTRO, DESTINO_INTERNET])

        self.assertTrue(resultados[0].tcp_ok)
        self.assertFalse(resultados[1].tcp_ok)


class TestDestinosDoServidorLinux(unittest.TestCase):
    def test_inclui_registro_configurado_docker_hub_e_internet(self):
        destinos = destinos_do_servidor_linux("registro.cliente.com.br:5050")

        self.assertEqual(destinos[0].endereco, "registro.cliente.com.br")
        self.assertEqual(destinos[0].porta, 5050)
        self.assertEqual([destino.endereco for destino in destinos[1:]],
                         ["registry-1.docker.io", "google.com"])


class TestMensagemSemRede(unittest.TestCase):
    def test_lista_destinos_e_resultado_do_ping(self):
        resultados = verificar_destinos(
            ExecutorFake(codigo_padrao=1),
            [DESTINO_REGISTRO, DESTINO_INTERNET]
        )

        mensagem = montar_mensagem_sem_rede(resultados, ping_respondeu=False)

        self.assertIn("gitlab.microuniverso.com.br:5050", mensagem)
        self.assertIn("google.com:443", mensagem)
        self.assertIn("ping google.com respondeu: False", mensagem)


if __name__ == "__main__":
    unittest.main()
