# VPM 20250309 desenvolvido por Victor Mammana, com apoio do GPT
import re
import sys
import json
import unicodedata
import spacy
import nltk
from nltk.tokenize import word_tokenize
from nltk.corpus import stopwords
import warnings
import logging

# Suprimir avisos do NLTK
warnings.filterwarnings("ignore", category=UserWarning, module="nltk")

# Suprimir logs do spaCy
logging.getLogger("nltk").setLevel(logging.CRITICAL)
logging.getLogger("spacy").setLevel(logging.ERROR)

# Baixa os recursos necessários do NLTK
#nltk.download('punkt', quiet=True)
#nltk.download('stopwords', quiet=True)

# Carregar modelo do spaCy
nlp = spacy.load("pt_core_news_sm")

# Dicionário de classificação de tokens
TOKEN_CLASSES = {
    "ADJ": "adjetivo",
    "ADV": "adverbio",
    "DET": "artigo",
    "NUM": "numeral",
    "CONJ": "conjuncao",
    "INTJ": "interjeicao",
    "NOUN": "substantivo",
    "PRON": "pronome",
    "PROPN": "substantivo",
    "PUNCT": "pontuacao",
    "SYM": "sigla",
    "ADP": "preposicao",
    "VERB": "verbo",
    "AUX": "verbo"
}

# Lista de palavras que devem ser substantivos
CORRECAO_MANUAL = {
    "facilitador": "NOUN",
    "coordenador": "NOUN",
    "engenheiro": "NOUN",
    "analista": "NOUN",
    "gestor": "NOUN",
    "supervisor": "NOUN",
    "desincrustador": "NOUN",
    "detergente": "NOUN"
}


def is_sigla(texto):
    # Remove espaços extras (trim)
    texto = texto.strip()

    # Lista de exceções que não são siglas
    excecoes = {"voz", "arroz", "feroz", "abdomen", "chef", "jigsaw", "latex", "show", "complex", "calif", "subsequente", "1º", "1º","2º", "3º", "administracao", "internet"}

    # Define padrões comuns de encontros consonantais
    encontros_consonantais_comuns = {
        "bl", "br", "cl", "cr", "dr", "fl", "fr", "gl", "gr",
        "pl", "pr", "tr", "vl", "ch", "lh", "nh", "ss", "rr", "gu", "qu", "rs", "ns", "ps", "ts", "st", "ct", "lt", "nc", "sc", "sm", "sp", "st", "pt", "ct", "rt", "lt", "mp", "mb", "nd", "ng", "nk", "nt", "nv", "mn", "cn", "lv", "rn", "rl", "rd", "rf", "rp", "rv", "rg", "rk", "rm", "rs", "rt", "rc", "rb", "rz", "rç", "rj", "nf", "dm", "dv", "dz", "dl", "dm", "db", "dc", "dd", "df", "dg", "dk", "dl", "dm", "dn", "dp", "dr", "ds", "dt", "dv", "dw", "dx", "dz"
    }

    # Encontros consonantais comuns de três letras
    trigramas_comuns = {
        "str", "scr", "spr", "spl", "sfr", "ntr", "ctr", "mbr", "ndr", "ltr", "ngl"
    }

    # Encontros consonantais comuns de quatro letras
    tetragramas_comuns = {
        "trans", "extr", "entr", "subt", "desp", "circ", "pros"
    }

    # Definição de consoantes e vogais
    consoantes = "bcdfghjklmnpqrstvwxyz"
    vogais = "aeiou"

    if len(texto) == 1 and not texto.isalnum():
        return False  # Apenas um símbolo, não é sigla

    if texto.isdigit():
        return False  # Apenas números, não é sigla

#    if re.fullmatch(r"[A-Z]{2,3}", texto):
#        return True


    # Remove acentos e normaliza para minúsculas
    texto_normalizado = ''.join(
        c for c in unicodedata.normalize('NFD', texto.lower()) if unicodedata.category(c) != 'Mn'
    )

    # Se a palavra estiver na lista de exceções, **não é sigla**
    if texto_normalizado in excecoes:
        return False
    if any(c.isdigit() for c in texto) and any(c.isalpha() for c in texto):
        return True  # Contém números e letras, é sigla


    # Conta número total de vogais e consoantes
    num_vogais = sum(1 for c in texto_normalizado if c in vogais)
    num_consoantes = sum(1 for c in texto_normalizado if c in consoantes)

    # Se a string tiver menos de 8 caracteres, **é candidata** a sigla, mas precisa de mais verificações

    if len(texto_normalizado) > 8 and num_vogais < 3:
        return True
    
    if num_vogais == 0:
        return True

    # Se tiver mais de 5 consoantes seguidas → É sigla
    if re.search(r"[" + consoantes + "]{6,}", texto_normalizado):
        return True

    # Se tiver 4 consoantes seguidas e menos de 2 vogais → É sigla
    if re.search(r"[" + consoantes + "]{4}", texto_normalizado) and num_vogais < 2:
        return True

    # Verifica 4, 3 ou 2 consoantes seguidas fora dos encontros comuns → É sigla
    for match in re.finditer(r"[" + consoantes + "]{2,4}", texto_normalizado):
        grupo = match.group()
        if grupo not in encontros_consonantais_comuns and grupo not in trigramas_comuns and grupo not in tetragramas_comuns:
            return True

    # Se terminar com uma consoante improvável → É sigla
    consoantes_finais_improvaveis = "bcdfgjkpqtvwz"  # Removemos algumas que ocorrem naturalmente (como "l", "s", "n")
    if texto_normalizado[-1] in consoantes_finais_improvaveis:
        return True

    # Se terminar com duas consoantes seguidas → É sigla
    if len(texto_normalizado) > 1 and texto_normalizado[-1] in consoantes and texto_normalizado[-2] in consoantes:
        return True

    # Se houver sequência de vogais improváveis (cinco ou mais seguidas) → É sigla
    if re.search(r"[aeiou]{5,}", texto_normalizado):
        return True

    # Se houver sílabas não pronunciáveis (Ex: "rtm", "tm", "mt")  É sigla
    padroes_silabas_impronunciaveis = {"tm", "rt", "mt", "pt", "ct", "mn", "sr", "sm", "ts", "sp", "cp", "ps", "st", "rs", "ns", "ls", "lt", "rt", "rd", "rc", "rp", "rv", "rg", "rk", "rm", "rn", "rl", "rf", "rz", "rç", "rj", "nf", "dm", "dv", "dz", "dl", "dm", "db", "dc", "dd", "df", "dg", "dk", "dl", "dm", "dn", "dp", "dr", "ds", "dt", "dv", "dw", "dx", "dz"}
#    for padrao in padroes_silabas_impronunciaveis:
#        if padrao in texto_normalizado:
#            return True

    # Se começar com duas consoantes improváveis → É sigla
    if len(texto_normalizado) > 1 and texto_normalizado[:2] in padroes_silabas_impronunciaveis:
        return True
    # Se começar com duas consoantes improváveis → É sigla e tiver 3 ou menos caracteres REDUNDANTE
    if len(texto_normalizado) < 4 and texto_normalizado[:2] in padroes_silabas_impronunciaveis:
        return True

    # Se a palavra é **candidata a sigla** (menor que 8 caracteres) e passou em pelo menos um critério → É sigla
    return False



# Função para normalizar texto (remover acentos)
def normalize_text(text):
    return ''.join(c for c in unicodedata.normalize('NFD', text.lower()) if unicodedata.category(c) != 'Mn')

# Processar frase e retornar tokens com POS tags traduzidas
def processar_frase(frase, conta, classificacao, coluna, frase_pura):
    doc = nlp(frase)  # Processa a frase com spaCy
    tokens_result = []

    for token in doc:
        if is_sigla(token.text):
            tokens_result.append({
                "token": token.text,
                "tipo": "sigla"
            })
            continue
        token_acentuado = token.text.lower()

        # Filtrar stopwords
        #if token_acentuado in stopwords.words('portuguese'):
        #    continue

        token_normalizado = normalize_text(token_acentuado)
        token_pos = CORRECAO_MANUAL.get(token_acentuado, token.pos_)  # Corrigir se estiver na lista
        tipo_token = TOKEN_CLASSES.get(token_pos, "substantivo")  # Traduzir POS tag
        tipo_token_original = token.pos_
        explicacao =  spacy.explain(token.pos_)
        lemma = token.lemma_

        tokens_result.append({
            "token": token_acentuado,
            "tipo": tipo_token,
            "explicacao": explicacao,
            "lemma": lemma,
            "tipo_original": tipo_token_original
        })

    return {"conta": conta, "classificacao": classificacao, "coluna": coluna, "frase_pura": frase_pura, "frase": frase, "tokens": tokens_result} 

# Verifica se o script foi chamado com uma frase como argumento
if __name__ == "__main__":
    if len(sys.argv) < 3:
        print(json.dumps({"erro": "Usage python3 NLP_inserts.py \"<frase>\" <conta> \"<classificacao>\" <coluna> \"<frase_pura>\""}))
        sys.exit(1)

    frase = sys.argv[1]
    conta = sys.argv[2]
    classificacao = sys.argv[3]
    coluna = sys.argv[4]
    frase_pura = sys.argv[5]
    resultado = processar_frase(frase, conta, classificacao, coluna, frase_pura)
    print(json.dumps(resultado, ensure_ascii=False))

