import unittest
from pathlib import Path
from tempfile import TemporaryDirectory

import yaml

from lib.criptografia_senha import criptografar
from lib.preparar_configuracao_linux import (
    aplicar_variaveis,
    atualizar_configuracao_pelo_modelo,
    carregar_yaml,
    preparar_configuracao,
)


class TestPrepararConfiguracaoLinux(unittest.TestCase):
    def test_atualizar_configuracao_pelo_modelo_preserva_valores_e_adiciona_propriedades(self):
        modelo = {
            "versao": "2",
            "enterprise": {
                "configurar": False,
                "porta": 9988,
                "novaPropriedade": "valor-modelo",
            },
            "api-gateway": {
                "backends": [
                    {
                        "nome": "ApiEnterprise",
                        "host": "HOST_MODELO",
                    }
                ]
            },
        }
        atual = {
            "versao": "1",
            "enterprise": {
                "configurar": True,
                "porta": 7777,
            },
            "api-gateway": {
                "backends": [
                    {
                        "nome": "ApiEnterprise",
                        "host": "HOST_ATUAL",
                    }
                ]
            },
            "propriedadeExistenteSoNoAtual": "preservada",
        }

        configuracao = atualizar_configuracao_pelo_modelo(modelo, atual)

        self.assertEqual(configuracao["versao"], "2")
        self.assertTrue(configuracao["enterprise"]["configurar"])
        self.assertEqual(configuracao["enterprise"]["porta"], 7777)
        self.assertEqual(configuracao["enterprise"]["novaPropriedade"], "valor-modelo")
        self.assertEqual(configuracao["api-gateway"]["backends"][0]["host"], "HOST_ATUAL")
        self.assertEqual(configuracao["propriedadeExistenteSoNoAtual"], "preservada")

    def test_preparar_configuracao_aplica_env_e_criptografa_campos_padrao(self):
        with TemporaryDirectory() as tmpdir:
            tmpdir = Path(tmpdir)
            modelo = tmpdir / "modelo.yaml"
            destino = tmpdir / "microuniversoWeb.yaml"
            env = tmpdir / "config.env"

            _salvar_yaml(modelo, _modelo_minimo())
            env.write_text(
                "\n".join(
                    [
                        "acessos.servidorLinux.configurar=true",
                        "acessos.servidorLinux.tipoConexao=LINUX",
                        "acessos.servidorLinux.senha=senha-linux",
                        "acessos.banco.senha=senha-banco",
                        "acessos.usuarioAdministrador.senha=senha-admin",
                        "acessos.registroDocker.senha=token-normal",
                        "enterprise.porta=7777",
                        "api-gateway.backends[0].host=enterprise",
                    ]
                ),
                encoding="utf-8",
            )

            resultado = preparar_configuracao(modelo, destino, env)
            configuracao = carregar_yaml(destino)

            self.assertTrue(configuracao["acessos"]["servidorLinux"]["configurar"])
            self.assertEqual(configuracao["acessos"]["servidorLinux"]["tipoConexao"], "LINUX")
            self.assertEqual(configuracao["acessos"]["servidorLinux"]["senha"], criptografar("senha-linux"))
            self.assertEqual(configuracao["acessos"]["banco"]["senha"], criptografar("senha-banco"))
            self.assertEqual(configuracao["acessos"]["usuarioAdministrador"]["senha"], criptografar("senha-admin"))
            self.assertEqual(configuracao["acessos"]["registroDocker"]["senha"], "token-normal")
            self.assertEqual(configuracao["enterprise"]["porta"], 7777)
            self.assertEqual(configuracao["api-gateway"]["backends"][0]["host"], "enterprise")
            self.assertCountEqual(
                resultado.campos_criptografados,
                [
                    "acessos.banco.senha",
                    "acessos.usuarioAdministrador.senha",
                    "acessos.servidorLinux.senha",
                ],
            )

    def test_preparar_configuracao_nao_criptografa_senha_duas_vezes(self):
        with TemporaryDirectory() as tmpdir:
            tmpdir = Path(tmpdir)
            modelo = tmpdir / "modelo.yaml"
            destino = tmpdir / "microuniversoWeb.yaml"

            configuracao = _modelo_minimo()
            configuracao["acessos"]["banco"]["senha"] = criptografar("senha-banco")
            configuracao["acessos"]["usuarioAdministrador"]["senha"] = criptografar("senha-admin")
            configuracao["acessos"]["servidorLinux"]["senha"] = criptografar("senha-linux")

            _salvar_yaml(modelo, _modelo_minimo())
            _salvar_yaml(destino, configuracao)

            resultado = preparar_configuracao(modelo, destino, gerar_backup=False)
            configuracao_final = carregar_yaml(destino)

            self.assertEqual(configuracao_final["acessos"]["banco"]["senha"], criptografar("senha-banco"))
            self.assertEqual(configuracao_final["acessos"]["usuarioAdministrador"]["senha"], criptografar("senha-admin"))
            self.assertEqual(configuracao_final["acessos"]["servidorLinux"]["senha"], criptografar("senha-linux"))
            self.assertEqual(resultado.campos_criptografados, [])

    def test_aplicar_variaveis_suporta_chave_com_hifen_e_lista(self):
        configuracao = _modelo_minimo()

        aplicar_variaveis(
            configuracao,
            {
                "api-gateway.backends[0].host": "enterprise",
                "api-gateway.backends[0].porta": "5000",
            },
        )

        self.assertEqual(configuracao["api-gateway"]["backends"][0]["host"], "enterprise")
        self.assertEqual(configuracao["api-gateway"]["backends"][0]["porta"], 5000)

    def test_aplicar_variaveis_falha_quando_caminho_nao_existe(self):
        with self.assertRaises(KeyError):
            aplicar_variaveis(_modelo_minimo(), {"enterprise.naoExiste": "x"})


def _modelo_minimo():
    return {
        "versao": "1",
        "acessos": {
            "servidorLinux": {
                "configurar": False,
                "host": "localhost",
                "usuario": "usuario",
                "senha": "SENHA",
                "tipoConexao": "NAO_DEFINIDO",
                "forcarUsoRedeExterna": False,
            },
            "banco": {
                "host": "localhost",
                "usuario": "sa",
                "senha": "SENHA",
                "nomeBanco": "microuniverso",
            },
            "usuarioAdministrador": {
                "usuario": "admin",
                "senha": "SENHA",
            },
            "registroDocker": {
                "usuario": "oauth2",
                "senha": "TOKEN",
            },
        },
        "enterprise": {
            "configurar": False,
            "porta": 9988,
        },
        "api-gateway": {
            "backends": [
                {
                    "nome": "ApiEnterprise",
                    "host": "HOST",
                    "porta": 9988,
                }
            ]
        },
    }


def _salvar_yaml(caminho, configuracao):
    with Path(caminho).open("w", encoding="utf-8") as arquivo:
        yaml.safe_dump(configuracao, arquivo, allow_unicode=True, sort_keys=False)


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