import os
import unittest

from lib.mapa_dependencias import (
    ErroMapaDependencias,
    carregar_mapa_dependencias,
    interpretar_mapa_dependencias,
    validar_dependencias,
)

DIRETORIO_SCRIPTS_IMPLANTACAO = os.path.abspath(
    os.path.join(os.path.dirname(__file__), "..", "..")
)


def montar_mapa(bidirecional=True):
    return interpretar_mapa_dependencias({
        "versao": 1,
        "regras": [
            {
                "id": "enterprise-5.4.9-central-de-relatorios-1.6.0",
                "descricao": "Precisam subir juntas.",
                "bidirecional": bidirecional,
                "quando": {"sistema": "enterprise", "versaoMinima": "5.4.9"},
                "exige": {"sistema": "centralDeRelatorios", "versaoMinima": "1.6.0"},
            }
        ],
    })


class TestInterpretarMapaDependencias(unittest.TestCase):
    def test_interpreta_regra_completa(self):
        regras = montar_mapa()

        self.assertEqual(len(regras), 1)
        self.assertEqual(regras[0].identificador, "enterprise-5.4.9-central-de-relatorios-1.6.0")
        self.assertEqual(regras[0].quando.sistema, "enterprise")
        self.assertEqual(regras[0].quando.versao_minima, "5.4.9")
        self.assertEqual(regras[0].exige.sistema, "centralDeRelatorios")
        self.assertEqual(regras[0].exige.versao_minima, "1.6.0")
        self.assertTrue(regras[0].bidirecional)

    def test_mapa_vazio_nao_gera_regra(self):
        self.assertEqual(interpretar_mapa_dependencias(None), [])
        self.assertEqual(interpretar_mapa_dependencias({}), [])
        self.assertEqual(interpretar_mapa_dependencias({"versao": 1, "regras": []}), [])

    def test_sistema_desconhecido_e_recusado(self):
        with self.assertRaises(ErroMapaDependencias) as contexto:
            interpretar_mapa_dependencias({
                "regras": [{
                    "id": "regra-invalida",
                    "quando": {"sistema": "sistemaQueNaoExiste", "versaoMinima": "1.0.0"},
                    "exige": {"sistema": "centralDeRelatorios", "versaoMinima": "1.0.0"},
                }]
            })

        self.assertIn("sistemaQueNaoExiste", str(contexto.exception))

    def test_bloco_ausente_e_recusado(self):
        with self.assertRaises(ErroMapaDependencias):
            interpretar_mapa_dependencias({
                "regras": [{"id": "sem-exige", "quando": {"sistema": "enterprise", "versaoMinima": "1.0.0"}}]
            })

    def test_versao_minima_ausente_e_recusada(self):
        with self.assertRaises(ErroMapaDependencias):
            interpretar_mapa_dependencias({
                "regras": [{
                    "id": "sem-versao",
                    "quando": {"sistema": "enterprise"},
                    "exige": {"sistema": "centralDeRelatorios", "versaoMinima": "1.0.0"},
                }]
            })

    def test_id_duplicado_e_recusado(self):
        regra = {
            "id": "repetida",
            "quando": {"sistema": "enterprise", "versaoMinima": "1.0.0"},
            "exige": {"sistema": "centralDeRelatorios", "versaoMinima": "1.0.0"},
        }

        with self.assertRaises(ErroMapaDependencias):
            interpretar_mapa_dependencias({"regras": [regra, dict(regra)]})


class TestValidarDependencias(unittest.TestCase):
    def test_par_novo_e_valido(self):
        violacoes = validar_dependencias(
            {"enterprise": "5.4.9-linux", "centralDeRelatorios": "1.6.0"},
            montar_mapa()
        )

        self.assertEqual(violacoes, [])

    def test_par_antigo_e_valido(self):
        violacoes = validar_dependencias(
            {"enterprise": "5.4.7-linux", "centralDeRelatorios": "1.5.0"},
            montar_mapa()
        )

        self.assertEqual(violacoes, [])

    def test_enterprise_nova_com_relatorios_antigo_e_invalido(self):
        violacoes = validar_dependencias(
            {"enterprise": "5.4.9-linux", "centralDeRelatorios": "1.5.0"},
            montar_mapa()
        )

        self.assertEqual(len(violacoes), 1)
        self.assertIn("API Enterprise na versao 5.4.9-linux", violacoes[0].mensagem())
        self.assertIn("Central de Relatorios na versao 1.6.0 ou superior", violacoes[0].mensagem())

    def test_relatorios_novo_com_enterprise_antiga_e_invalido(self):
        violacoes = validar_dependencias(
            {"enterprise": "5.4.7-linux", "centralDeRelatorios": "1.6.0"},
            montar_mapa()
        )

        self.assertEqual(len(violacoes), 1)
        self.assertIn("Central de Relatorios na versao 1.6.0", violacoes[0].mensagem())
        self.assertIn("API Enterprise na versao 5.4.9 ou superior", violacoes[0].mensagem())

    def test_regra_nao_bidirecional_so_cobra_um_sentido(self):
        mapa = montar_mapa(bidirecional=False)

        self.assertEqual(
            len(validar_dependencias(
                {"enterprise": "5.4.9-linux", "centralDeRelatorios": "1.5.0"},
                mapa
            )),
            1
        )
        self.assertEqual(
            validar_dependencias(
                {"enterprise": "5.4.7-linux", "centralDeRelatorios": "1.6.0"},
                mapa
            ),
            []
        )

    def test_versao_acima_da_minima_tambem_dispara_a_regra(self):
        violacoes = validar_dependencias(
            {"enterprise": "5.6.0-linux", "centralDeRelatorios": "1.4.0"},
            montar_mapa()
        )

        self.assertEqual(len(violacoes), 1)

    def test_sistema_nao_configurado_faz_a_regra_ser_ignorada(self):
        self.assertEqual(
            validar_dependencias({"enterprise": "5.4.9-linux"}, montar_mapa()),
            []
        )
        self.assertEqual(
            validar_dependencias({"centralDeRelatorios": "1.5.0"}, montar_mapa()),
            []
        )
        self.assertEqual(validar_dependencias({}, montar_mapa()), [])

    def test_versao_de_branch_faz_a_regra_ser_ignorada(self):
        violacoes = validar_dependencias(
            {"enterprise": "FNX-1234-linux", "centralDeRelatorios": "1.5.0"},
            montar_mapa()
        )

        self.assertEqual(violacoes, [])

    def test_filtro_por_sistema_avalia_apenas_regras_relacionadas(self):
        versoes = {"enterprise": "5.4.9-linux", "centralDeRelatorios": "1.5.0"}
        mapa = montar_mapa()

        self.assertEqual(len(validar_dependencias(versoes, mapa, chave_sistema="enterprise")), 1)
        self.assertEqual(len(validar_dependencias(versoes, mapa, chave_sistema="centralDeRelatorios")), 1)
        self.assertEqual(validar_dependencias(versoes, mapa, chave_sistema="coletaPrecos"), [])


class TestArquivoMapaDependenciasDoRepositorio(unittest.TestCase):
    def test_arquivo_do_repositorio_carrega_e_e_valido(self):
        regras = carregar_mapa_dependencias(DIRETORIO_SCRIPTS_IMPLANTACAO)

        self.assertTrue(len(regras) >= 1)

        identificadores = [regra.identificador for regra in regras]

        self.assertIn("enterprise-5.4.9-central-de-relatorios-1.6.0", identificadores)

    def test_regra_do_repositorio_bloqueia_o_cenario_relatado(self):
        regras = carregar_mapa_dependencias(DIRETORIO_SCRIPTS_IMPLANTACAO)

        # enterprise >= 5.4.9-linux com relatorios < 1.6.0 da erro
        self.assertEqual(
            len(validar_dependencias(
                {"enterprise": "5.4.9-linux", "centralDeRelatorios": "1.5.0"},
                regras
            )),
            1
        )

        # relatorios >= 1.6.0 com enterprise < 5.4.9-linux da erro
        self.assertEqual(
            len(validar_dependencias(
                {"enterprise": "5.4.8-linux", "centralDeRelatorios": "1.6.0"},
                regras
            )),
            1
        )

        # o par correto passa
        self.assertEqual(
            validar_dependencias(
                {"enterprise": "5.4.9-linux", "centralDeRelatorios": "1.6.0"},
                regras
            ),
            []
        )

    def test_mapa_inexistente_retorna_lista_vazia(self):
        self.assertEqual(carregar_mapa_dependencias(r"c:\diretorio\que\nao\existe"), [])


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