"""
Catalogo dos sistemas distribuidos como imagem Docker.

Cada sistema declara onde ficam, no YAML de configuracao:

- a chave que liga/desliga a configuracao (`caminho_configurar`);
- a versao/tag da imagem (`caminho_versao`);
- o nome da imagem, fixo no compose ou configuravel (`imagem`/`caminho_imagem`);
- as credenciais do registro, quando diferentes de `acessos.registroDocker`.

Esse catalogo e a fonte unica usada para:

- resolver `master` na versao oficial mais recente do registro;
- validar que a tag informada existe antes de tentar atualizar;
- avaliar o mapa de dependencias entre sistemas.
"""

import re


CAMINHO_USUARIO_REGISTRO_PADRAO = ("acessos", "registroDocker", "usuario")
CAMINHO_SENHA_REGISTRO_PADRAO = ("acessos", "registroDocker", "senha")

REGISTRO_EMPRESA = "gitlab.microuniverso.com.br:5050"

# Valores que existem no modelo apenas como marcador e nunca sao tag real
_PLACEHOLDER = re.compile(r"^[A-Z][A-Z0-9_]*$")


def eh_valor_placeholder(valor):
    """Indica se o valor e um marcador do modelo (ex.: VERSAO_ENTERPRISE)."""

    if valor is None:
        return False

    texto = str(valor).strip()

    if not texto:
        return False

    return bool(_PLACEHOLDER.match(texto))


def obter_valor_caminho(configuracoes, caminho):
    """Le um valor aninhado do YAML. Retorna None quando o caminho nao existe."""

    atual = configuracoes

    for chave in caminho:
        if not isinstance(atual, dict) or chave not in atual:
            return None

        atual = atual[chave]

    return atual


def _obter_bool_caminho(configuracoes, caminho):
    valor = obter_valor_caminho(configuracoes, caminho)

    if isinstance(valor, bool):
        return valor

    if valor is None:
        return False

    return str(valor).strip().lower() in ["true", "s", "sim", "1"]


class SistemaDocker():
    def __init__(
        self,
        chave,
        nome,
        caminho_versao,
        caminho_configurar,
        imagem=None,
        caminho_imagem=None,
        plataforma=None,
        caminho_usuario_registro=CAMINHO_USUARIO_REGISTRO_PADRAO,
        caminho_senha_registro=CAMINHO_SENHA_REGISTRO_PADRAO,
        depende_servidor_linux=True
    ):
        self.chave = chave
        self.nome = nome
        self.caminho_versao = caminho_versao
        self.caminho_configurar = caminho_configurar
        self.imagem = imagem
        self.caminho_imagem = caminho_imagem
        self.plataforma = plataforma
        self.caminho_usuario_registro = caminho_usuario_registro
        self.caminho_senha_registro = caminho_senha_registro
        self.depende_servidor_linux = depende_servidor_linux

    def esta_configurado(self, configuracoes):
        if self.depende_servidor_linux:
            if not _obter_bool_caminho(configuracoes, ("acessos", "servidorLinux", "configurar")):
                return False

        return _obter_bool_caminho(configuracoes, self.caminho_configurar)

    def obter_versao(self, configuracoes):
        valor = obter_valor_caminho(configuracoes, self.caminho_versao)

        return "" if valor is None else str(valor).strip()

    def caminho_versao_texto(self):
        """
        Caminho da versao no YAML, para a mensagem de erro apontar onde corrigir.

        Exemplo: "enterprise.imagem.versao".
        """

        return ".".join(self.caminho_versao)

    def obter_imagem(self, configuracoes):
        if self.caminho_imagem:
            valor = obter_valor_caminho(configuracoes, self.caminho_imagem)

            if valor:
                return str(valor).strip()

        return self.imagem

    def obter_plataforma(self, configuracoes):
        return self.plataforma

    def obter_credenciais_registro(self, configuracoes):
        """
        Credenciais do registro para este sistema.

        Retorna a lista de pares (usuario, senha) a tentar, em ordem. As
        credenciais especificas do sistema vem primeiro, mas as credenciais
        gerais de `acessos.registroDocker` entram como alternativa porque os
        tokens especificos de alguns sistemas costumam ficar desatualizados nas
        configuracoes existentes.
        """

        credenciais = []

        usuario = obter_valor_caminho(configuracoes, self.caminho_usuario_registro)
        senha = obter_valor_caminho(configuracoes, self.caminho_senha_registro)

        if usuario and senha:
            credenciais.append((usuario, senha))

        usuario_padrao = obter_valor_caminho(configuracoes, CAMINHO_USUARIO_REGISTRO_PADRAO)
        senha_padrao = obter_valor_caminho(configuracoes, CAMINHO_SENHA_REGISTRO_PADRAO)

        if usuario_padrao and senha_padrao and (usuario_padrao, senha_padrao) not in credenciais:
            credenciais.append((usuario_padrao, senha_padrao))

        return credenciais

    def __repr__(self):
        return f"SistemaDocker({self.chave!r})"


# A Enterprise e o renderizador ficam fixados em `plataforma="linux"` porque os
# configuradores Python sobem apenas os composes Linux. O deploy Windows da
# Enterprise e feito pelo configurarEnterprise.ps1, fora deste catalogo. Sem
# esse filtro, resolver `master` poderia escolher uma tag `-windows`.
SISTEMAS_DOCKER = (
    SistemaDocker(
        chave="enterprise",
        nome="API Enterprise",
        caminho_versao=("enterprise", "imagem", "versao"),
        caminho_configurar=("enterprise", "configurar"),
        caminho_imagem=("enterprise", "imagem", "nome"),
        plataforma="linux",
        caminho_usuario_registro=("enterprise", "imagem", "usuario"),
        caminho_senha_registro=("enterprise", "imagem", "senha"),
    ),
    SistemaDocker(
        chave="enterpriseRenderizador",
        nome="Renderizador da API Enterprise",
        caminho_versao=("enterprise", "imagem", "versao"),
        caminho_configurar=("enterprise", "renderizador", "configurar"),
        caminho_imagem=("enterprise", "renderizador", "imagem", "nome"),
        plataforma="linux",
        caminho_usuario_registro=("enterprise", "imagem", "usuario"),
        caminho_senha_registro=("enterprise", "imagem", "senha"),
    ),
    SistemaDocker(
        chave="centralDeCadastros",
        nome="Central de Cadastros",
        caminho_versao=("centralDeCadastros", "versao"),
        caminho_configurar=("centralDeCadastros", "configurar"),
        imagem=f"{REGISTRO_EMPRESA}/microuniverso/mufenix/centraldecadastrosfrontend/central-de-cadastros-producao",
    ),
    SistemaDocker(
        chave="centralDeCadastrosStorybook",
        nome="Storybook da Central de Cadastros",
        caminho_versao=("centralDeCadastros", "storybook", "versao"),
        caminho_configurar=("centralDeCadastros", "storybook", "configurar"),
        imagem=f"{REGISTRO_EMPRESA}/microuniverso/mufenix/centraldecadastrosfrontend/central-de-cadastros-storybook",
    ),
    SistemaDocker(
        chave="centralDeRelatorios",
        nome="Central de Relatorios",
        caminho_versao=("centralDeRelatorios", "versao"),
        caminho_configurar=("centralDeRelatorios", "configurar"),
        imagem=f"{REGISTRO_EMPRESA}/microuniverso/mufenix/centralrelatorios/central-de-relatorios",
    ),
    SistemaDocker(
        chave="centralDeRelatoriosStorybook",
        nome="Storybook da Central de Relatorios",
        caminho_versao=("centralDeRelatorios", "storybook", "versao"),
        caminho_configurar=("centralDeRelatorios", "storybook", "configurar"),
        imagem=f"{REGISTRO_EMPRESA}/microuniverso/mufenix/centralrelatorios/central-de-relatorios-storybook",
    ),
    SistemaDocker(
        chave="pesquisaProduto",
        nome="Pesquisa de Produto",
        caminho_versao=("pesquisaProduto", "versao"),
        caminho_configurar=("pesquisaProduto", "configurar"),
        imagem=f"{REGISTRO_EMPRESA}/microuniverso/mufenix/pesquisaproduto/pesquisa-produto",
    ),
    SistemaDocker(
        chave="pesquisaProdutoStorybook",
        nome="Storybook da Pesquisa de Produto",
        caminho_versao=("pesquisaProduto", "storybook", "versao"),
        caminho_configurar=("pesquisaProduto", "storybook", "configurar"),
        imagem=f"{REGISTRO_EMPRESA}/microuniverso/mufenix/pesquisaproduto/pesquisa-produto",
    ),
    SistemaDocker(
        chave="coletaPrecos",
        nome="Coleta de Precos",
        caminho_versao=("coletaPrecos", "versao"),
        caminho_configurar=("coletaPrecos", "configurar"),
        imagem=f"{REGISTRO_EMPRESA}/microuniverso/mufenix/coletaprecos/coleta-preco",
    ),
    SistemaDocker(
        chave="intercompany",
        nome="Intercompany",
        caminho_versao=("intercompany", "versao"),
        caminho_configurar=("intercompany", "configurar"),
        imagem=f"{REGISTRO_EMPRESA}/microuniverso/mufenix/intercompanyfrontend/inter-company-frontend-releases",
    ),
    SistemaDocker(
        chave="intercompanyStorybook",
        nome="Storybook do Intercompany",
        caminho_versao=("intercompany", "storybook", "versao"),
        caminho_configurar=("intercompany", "storybook", "configurar"),
        imagem=f"{REGISTRO_EMPRESA}/microuniverso/mufenix/intercompanyfrontend/inter-company-storybook-releases",
    ),
    SistemaDocker(
        chave="configuracoesWeb",
        nome="Configuracoes Web",
        caminho_versao=("configuracoesWeb", "versao"),
        caminho_configurar=("muWhatsapp", "configurarApi"),
        imagem=f"{REGISTRO_EMPRESA}/microuniverso/mufenix/configuracoesweb/configuracoes-web",
    ),
    SistemaDocker(
        chave="muWhatsapp",
        nome="Mu Whatsapp API",
        caminho_versao=("muWhatsapp", "versao"),
        caminho_configurar=("muWhatsapp", "configurarApi"),
        imagem=f"{REGISTRO_EMPRESA}/microuniverso/mufenix/wppconnect-server/muwhatsapp",
    ),
    SistemaDocker(
        chave="microkafka",
        nome="Microkafka",
        caminho_versao=("microkafka", "versaoMicrokafka"),
        caminho_configurar=("microkafka", "configurar"),
        imagem=f"{REGISTRO_EMPRESA}/microuniverso/mufenix/microkafka/microkafka",
    ),
    SistemaDocker(
        chave="microkafkaConfigurador",
        nome="Configurador do Microkafka",
        caminho_versao=("microkafka", "versaoConfigurarMicrokafka"),
        caminho_configurar=("microkafka", "configurar"),
        imagem=f"{REGISTRO_EMPRESA}/microuniverso/mufenix/microkafka/configurar-microkafka",
    ),
    SistemaDocker(
        chave="vendasStorybook",
        nome="Storybook de Vendas",
        caminho_versao=("servicos", "orcamentoWeb", "storybook", "versao"),
        caminho_configurar=("servicos", "orcamentoWeb", "storybook", "configurar"),
        imagem=f"{REGISTRO_EMPRESA}/microuniverso/mufenix/projetoweb/vendas-storybook",
    ),
    SistemaDocker(
        chave="monitorFreteRapido",
        nome="Monitor Frete Rapido",
        caminho_versao=("monitorFreteRapido", "imagem", "versao"),
        caminho_configurar=("monitorFreteRapido", "configurar"),
        caminho_imagem=("monitorFreteRapido", "imagem", "nome"),
        caminho_usuario_registro=("monitorFreteRapido", "imagem", "usuario"),
        caminho_senha_registro=("monitorFreteRapido", "imagem", "senha"),
        depende_servidor_linux=False,
    ),
)

SISTEMAS_DOCKER_POR_CHAVE = {sistema.chave: sistema for sistema in SISTEMAS_DOCKER}


def obter_sistema_docker(chave):
    if chave not in SISTEMAS_DOCKER_POR_CHAVE:
        raise ValueError(
            f"Sistema Docker '{chave}' nao esta no catalogo de sistemas_docker.py. "
            f"Sistemas conhecidos: {', '.join(sorted(SISTEMAS_DOCKER_POR_CHAVE))}"
        )

    return SISTEMAS_DOCKER_POR_CHAVE[chave]


def listar_sistemas_configurados(configuracoes):
    return [sistema for sistema in SISTEMAS_DOCKER if sistema.esta_configurado(configuracoes)]
