import os
import re
import sys
import json
import time
import email
import imaplib
import smtplib
import tempfile
import logging
import mimetypes
import io
import subprocess
import shutil
from pathlib import Path
from typing import Tuple, List, Dict, Optional
from email.header import decode_header, make_header
from email.message import EmailMessage
from email.utils import parseaddr, make_msgid
from email.mime.text import MIMEText
from email.mime.image import MIMEImage

from dotenv import load_dotenv
from docx import Document
from docx.shared import Cm
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.text import WD_TAB_ALIGNMENT

try:
    from openai import OpenAI, APIError
except ImportError:
    OpenAI = None
    APIError = None

try:
    import language_tool_python
except ImportError:
    language_tool_python = None

# ==============================
# Constantes e Configurações
# ==============================
PALAVRAS_PERSONALIZADAS = [
    'multicomputador', 'multiprocessador', 'Flynn', 'telemóvel', 'hardware',
    'software', 'backend', 'frontend', 'framework', 'INSUTEC', 'EISI',
    'Arquitetura', 'Computadores', 'SQL', 'script'
]

LISTA_PALAVRAS_INAPROPRIADAS = [
    'merda', 'porra', 'caralho', 'foder', 'puta', 'cona',
    'grosseira', 'estúpido', 'idiota'
]

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] - %(message)s",
    handlers=[logging.FileHandler("app.log", encoding='utf-8'), logging.StreamHandler(sys.stdout)]
)

load_dotenv()
IMAP_HOST = os.getenv("IMAP_HOST", "").strip()
IMAP_USER = os.getenv("IMAP_USER", "").strip()
IMAP_PASS = os.getenv("IMAP_PASS", "").strip()
IMAP_LABEL = os.getenv("IMAP_LABEL", "INBOX").strip()
SMTP_HOST = os.getenv("SMTP_HOST", "").strip()
SMTP_PORT = int(os.getenv("SMTP_PORT", "465"))
SMTP_USER = os.getenv("SMTP_USER", "").strip()
SMTP_PASS = os.getenv("SMTP_PASS", "").strip()
USE_SMTP_SSL = os.getenv("USE_SMTP_SSL", "true").lower() == "true"
OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "").strip()
OPENAI_MODEL = os.getenv("OPENAI_MODEL", "gpt-4o-mini").strip()
DISCIPLINA_FALLBACK = os.getenv("DISCIPLINA", "Não Identificada").strip()
ASSUNTO_PROVA_PALAVRA_CHAVE = os.getenv("ASSUNTO_PROVA_PREFIXO", "Enunciados").strip()
MAX_ISSUES = int(os.getenv("MAX_ISSUES", "15"))

EMAIL_APROVADO_PARA = os.getenv("EMAIL_APROVADO_PARA", "rouget.fundora@gmail.com").strip()
EMAIL_CC_GERAL = os.getenv("EMAIL_CC_GERAL", "rouget.ruano@insutec.ao").strip()

ASSINATURA = os.getenv("ASSINATURA", "Atenciosamente,\nEquipa de Validação").strip()

# --- CORREÇÃO DE CAMINHO ABSOLUTO (PARA ASSINATURA) ---
# Resolve o problema do "Ficheiro em falta no servidor"
# Obtém o diretório onde o script.py está (ex: /home/user/meu_script/)
SCRIPT_DIR = Path(__file__).resolve().parent

# Carrega o caminho RELATIVO da imagem do .env (ex: "img/assina.png")
SIGN_IMAGE_PATH_FROM_ENV = os.getenv("SIGN_IMAGE_PATH", "./img/assina.png").strip()

# Cria um caminho ABSOLUTO para a imagem
# (ex: /home/user/meu_script/img/assina.png)
SIGN_IMAGE_PATH_ABSOLUTE = SCRIPT_DIR / SIGN_IMAGE_PATH_FROM_ENV

# A variável global SIGN_IMAGE_PATH agora usa o caminho absoluto
SIGN_IMAGE_PATH = str(SIGN_IMAGE_PATH_ABSOLUTE)
# --- FIM DA CORREÇÃO ---

SIGN_IMAGE_WIDTH_CM = float(os.getenv("SIGN_IMAGE_WIDTH_CM", "5"))
REPLY_SENDER_ON_ERROR_FLAG = os.getenv("REPLY_SENDER_ON_ERROR", "true").lower() == "true"


# ==============================
# Funções Utilitárias
# ==============================
def clean_header(raw_header: Optional[str]) -> str:
    if not raw_header: return ""
    try: return str(make_header(decode_header(raw_header)))
    except Exception: return raw_header

def extract_full_docx_text(bytes_content: bytes) -> str:
    full_text = []
    with io.BytesIO(bytes_content) as file_stream:
        doc = Document(file_stream)
        for section in doc.sections:
            for header in (section.header, section.first_page_header, section.even_page_header,
                           section.footer, section.first_page_footer, section.even_page_footer):
                if header is None: continue
                for table in header.tables:
                    for row in table.rows:
                        for cell in row.cells:
                            full_text.append(cell.text)
                for paragraph in header.paragraphs:
                    full_text.append(paragraph.text)

        full_text.append("\n--- CONTEÚDO PRINCIPAL DO DOCUMENTO ---\n")

        for paragraph in doc.paragraphs:
            full_text.append(paragraph.text)
        for table in doc.tables:
            for row in table.rows:
                for cell in row.cells:
                    full_text.append(cell.text)

    return "\n".join(full_text).strip()

def spelling_issues_pt(text: str, custom_words: List[str]) -> List[Dict]:
    if language_tool_python is None: return []
    try:
        tool = language_tool_python.LanguageTool('pt-PT')
        
        if hasattr(tool, 'disabled_words'): tool.disabled_words.update(custom_words)
        elif hasattr(tool, 'disable_spellchecking_for_words'): tool.disable_spellchecking_for_words(custom_words)
        
        matches = tool.check(text or "")
        issues = []
        for m in matches:
            context = (text[max(0, m.offset - 30):m.offset] + f"⟦{text[m.offset:m.offset+m.errorLength]}⟧" + text[m.offset+m.errorLength:m.offset+m.errorLength+30])
            issues.append({"message": m.message, "context": context})
        return issues
    except Exception as e:
        logging.warning(f"LanguageTool indisponível: {e}."); return []

def check_for_profanity(text: str, word_list: List[str]) -> List[str]:
    text_lower = text.lower()
    found_words = set()
    for word in word_list:
        try:
            pattern = r'\b' + re.escape(word.lower()) + r'\b'
            if re.search(pattern, text_lower):
                found_words.add(word)
        except re.error as e:
            logging.warning(f"Erro de regex ao procurar por '{word}': {e}")
            
    return list(found_words)

# ====================================================================
# FUNÇÃO (evaluate_proof_content) - Com Critérios Pedagógicos
# ====================================================================
def evaluate_proof_content(text: str, fallback: str) -> Tuple[str, bool, str, str]:
    if not OPENAI_API_KEY:
        return fallback, False, "Validação OpenAI desativada (API_KEY ausente ou inválida no ficheiro .env).", ""
    client = OpenAI(api_key=OPENAI_API_KEY)

    prompt = f"""
    Aja como um Coordenador de Curso e especialista em pedagogia universitária em Angola.
    Analise o seguinte texto extraído de um documento Word (cabeçalho e corpo principal).

    **Contexto Linguístico (Importante):** O documento está em Português de Angola. Palavras como "telemóvel", "computador" ou "disciplina" são normais. A sua análise deve respeitar esta variante.

    Siga estas tarefas por ordem de PRIORIDADE:

    1.  **Identificar a Disciplina (TAREFA 1):** Leia o texto e encontre o nome EXATO da disciplina (ex: "Base de Dados I"). Dê prioridade ao cabeçalho.

    2.  **Análise de Profanidade (TAREFA 2 - CRÍTICA):** Verifique se o texto contém linguagem grosseira, imprópria, ofensiva ou profanidade (ex: "merda", "porra").
        * Se encontrar, "permissivel" DEVE ser `false` e a "justificativa" deve citar isto.

    3.  **Análise de Relevância (TAREFA 3 - CRÍTICA):** Analise CADA PERGUNTA. Verifique se TODAS as competências testadas (ex: Modelar ER, escrever SQL) são relevantes para a disciplina (TAREFA 1).
        * Se encontrar UMA ÚNICA pergunta de uma matéria COMPLETAMENTE DIFERENTE (ex: 'Teorema de Pitágoras' numa prova de 'Base de Dados'), "permissivel" DEVE ser `false`.

    ---
    **Critérios Pedagógicos (TAREFAS 4, 5, 6) - Afetam 'sugestoes', não 'permissivel'**
    ---

    4.  **Clareza e Ambiguidade (TAREFA 4 - Pedagógica):**
        * Verifique se as perguntas são claras e objectivas.
        * Se encontrar perguntas vagas (ex: "Fale sobre redes"), ambíguas ou subjectivas (ex: "O que acha de..."), deve apontá-las no campo "sugestoes".

    5.  **Nível Cognitivo (TAREFA 5 - Pedagógica):**
        * Verifique os verbos de ação (ex: "Defina", "Liste", "Analise", "Compare", "Crie", "Justifique").
        * Um bom enunciado deve ter um equilíbrio. Se o teste SÓ contiver verbos de nível baixo (Defina, O que é, Liste), aponte-o como uma sugestão de melhoria (ex: "Sugere-se incluir perguntas de análise ou aplicação, como 'Compare...' ou 'Justifique...'").

    6.  **Estrutura (TAREFA 6 - Pedagógica):**
        * Verifique se o enunciado parece bem estruturado (ex: perguntas numeradas, indicação de cotação/pontos).
        * Se faltar, adicione uma nota em "sugestoes".

    7.  **Gestão de Contexto Confuso (TAREFA 7 - IMPORTANTE):**
        * O enunciado pode ter erros de copiar/colar (ex: cenário 'Seguros' muda para 'Biblioteca', mas as *tarefas* (ex: 'Script SQL para acidentes') continuam relevantes para 'Seguros').
        * **NÃO REJEITE** se as *competências* (SQL, Modelação) forem relevantes para a disciplina. Considere "Permissível", mas aponte a confusão em "sugestoes".

    8.  **Decisão Final (TAREFA 8):**
        * "permissivel": `true` (APENAS se TAREFA 2 e 3 passarem, mesmo que 4, 5, 6 ou 7 gerem sugestões).
        * "permissivel": `false` (Se a TAREFA 2 ou 3 falharem).
        * "justificativa": A sua análise e a razão da decisão (focada nas TAREFAS 2 e 3 se for `false`).
        * "sugestoes": Sugestões de melhoria (das TAREFAS 4, 5, 6, 7). Se não houver, escreva "Nenhuma".

    Responda APENAS em JSON: {{"disciplina_identificada": "O nome EXATO encontrado", "permissivel": true|false, "justificativa": "A sua análise focada nos critérios de falha (Relevância e Profanidade).", "sugestoes": "Sugestões de melhoria pedagógica (Clareza, Nível Cognitivo, Estrutura) ou correção de contexto."}}
    """
    # === FIM DO PROMPT ===

    try:
        resp = client.chat.completions.create(
            model=OPENAI_MODEL,
            messages=[
                {"role": "system", "content": prompt},
                {"role": "user", "content": text}
            ],
            temperature=0.0,
            response_format={"type": "json_object"}
        )
        data = json.loads(resp.choices[0].message.content or "{}")
        return (
            data.get("disciplina_identificada", fallback),
            data.get("permissivel", False),
            data.get("justificativa", "A sua análise focada nos critérios de falha (Relevância e Profanidade)."),
            data.get("sugestoes", "Sugestões de melhoria pedagógica (Clareza, Nível Cognitivo, Estrutura) ou correção de contexto.")
        )
    except Exception as e:
        logging.error(f"Falha na API da OpenAI: {e}");
        return fallback, False, "Erro na comunicação com a IA.", ""


# ====================================================================
# Lógica do RODAPÉ (estável)
# ====================================================================
def add_signature_to_doc(doc_bytes: bytes, image_path_str: str, image_width_cm: float) -> Optional[bytes]:
    
    # A variável 'image_path_str' agora é o CAMINHO ABSOLUTO
    image_path = Path(image_path_str)
    
    if not image_path.is_file():
        # Este log agora mostrará o caminho absoluto, facilitando o debug
        logging.warning(f"Imagem de assinatura NÃO encontrada em {image_path_str}. A enviar documento original.")
        return None

    logging.info(f"Imagem de assinatura encontrada. A tentar limpar o RODAPÉ e inserir...")

    try:
        doc_stream = io.BytesIO(doc_bytes)
        doc = Document(doc_stream)

        section = doc.sections[0]
        footer = None

        if section.different_first_page_header_footer:
            logging.info("Documento usa 'Rodapé de Primeira Página Diferente'. A modificar esse.")
            footer = section.first_page_footer
        else:
            logging.info("Documento usa rodapé principal. A modificar esse.")
            footer = section.footer

        ftr_element = footer._element
        logging.info("A executar limpeza 'nuclear' (a remover *todos* os elementos filhos do rodapé)...")

        for child in list(ftr_element):
            ftr_element.remove(child)

        logging.info("Rodapé limpo com sucesso: Todos os elementos filhos foram removidos.")

        paragraph = footer.add_paragraph()
        paragraph.alignment = WD_ALIGN_PARAGRAPH.RIGHT
        run = paragraph.add_run()
        run.add_picture(str(image_path), width=Cm(image_width_cm))

        logging.info("Assinatura inserida no rodapé limpo.")

        new_doc_stream = io.BytesIO()
        doc.save(new_doc_stream)

        return new_doc_stream.getvalue()

    except Exception as e:
        logging.error(f"Falha ao adicionar assinatura ao documento Word (no rodapé): {e}", exc_info=True)
        return None

# ====================================================================
# Conversão para PDF via LibreOffice (Linux)
# ====================================================================
def convert_docx_to_pdf_linux(docx_bytes: bytes) -> bytes:
    soffice_cmd = shutil.which("libreoffice") or shutil.which("soffice")
    if not soffice_cmd:
        raise FileNotFoundError("Executável 'libreoffice' ou 'soffice' não encontrado. Por favor, instale-o (sudo apt-get install libreoffice).")

    with tempfile.TemporaryDirectory() as temp_dir:
        temp_dir_path = Path(temp_dir)
        docx_path = temp_dir_path / "input.docx"

        with open(docx_path, "wb") as f:
            f.write(docx_bytes)

        cmd = [
            soffice_cmd,
            "--headless",
            "--convert-to", "pdf",
            "--outdir", temp_dir,
            str(docx_path)
        ]

        logging.info(f"A executar comando: {' '.join(cmd)}")
        try:
            result = subprocess.run(cmd, capture_output=True, text=True, timeout=30, check=True)
            logging.info(f"LibreOffice stdout: {result.stdout}")
            if result.stderr:
                logging.warning(f"LibreOffice stderr: {result.stderr}")
        except subprocess.CalledProcessError as e:
            logging.error(f"Falha na conversão do LibreOffice. Erro: {e.stderr}")
            raise
        except subprocess.TimeoutExpired:
            logging.error("Conversão do LibreOffice demorou demasiado (timeout).")
            raise

        pdf_path = temp_dir_path / "input.pdf"
        if not pdf_path.exists():
            raise FileNotFoundError(f"LibreOffice não criou o ficheiro PDF esperado em {pdf_path}")

        with open(pdf_path, "rb") as f:
            pdf_bytes = f.read()

        return pdf_bytes

# ====================================================================
# Função de E-mail (estável)
# ====================================================================
def send_email(
    to_addr: str,
    subject: str,
    body: str,
    attachments: List[Tuple[str, bytes]] = None,
    cc_addr: Optional[str] = None,
    assinatura: str = "",
    sign_image_path: str = "", # 'sign_image_path' agora é o caminho absoluto
    sign_image_width_cm: float = 5.0
):
    if not all([SMTP_HOST, SMTP_USER, SMTP_PASS]): raise ConnectionError("Configurações de SMTP ausentes.")

    msg = EmailMessage()
    msg["From"] = SMTP_USER
    msg["To"] = to_addr
    msg["Subject"] = subject
    if cc_addr:
        msg["Cc"] = cc_addr

    assinatura_html = ""
    image_cid_str_with_brackets = None

    if assinatura:
        assinatura_formatada = assinatura.replace('\n', '<br>')
        assinatura_html = f"<br><br><p>{assinatura_formatada}</p>"

    # 'sign_image_path' (absoluto) é usado aqui
    image_path_obj = Path(sign_image_path)
    if assinatura_html and image_path_obj.is_file():
        try:
            image_cid_str_with_brackets = make_msgid(domain="insutec.ao")
            image_cid_for_html = image_cid_str_with_brackets.strip('<>')
            width_px = int(sign_image_width_cm * 37.795)
            assinatura_html += f'<img src="cid:{image_cid_for_html}" width="{width_px}">'
        except Exception as e:
            logging.warning(f"Erro ao preparar imagem de assinatura: {e}")
            image_cid_str_with_brackets = None
    elif assinatura_html:
        logging.warning(f"Imagem de assinatura (para HTML) NÃO encontrada em {sign_image_path}")


    plain_body = f"{body}\n\n{assinatura}"
    msg.set_content(plain_body, 'plain', 'utf-8')

    body_formatado = body.replace('\n', '<br>')
    html_body = f"""
    <html>
    <body>
        <p>{body_formatado}</p>
        {assinatura_html}
    </body>
    </html>
    """
    msg.add_alternative(html_body, 'html', 'utf-8')


    if image_cid_str_with_brackets and image_path_obj.is_file():
        try:
            img_data = image_path_obj.read_bytes()
            img_part = MIMEImage(img_data)
            img_part.add_header('Content-ID', image_cid_str_with_brackets)
            img_part.add_header('Content-Disposition', 'inline', filename=image_path_obj.name)
            msg.attach(img_part)
        except Exception as e:
            logging.warning(f"Falha ao anexar imagem de assinatura {sign_image_path}: {e}")

    # Anexos (Relatório e Documento)
    if attachments:
        for fname, fbytes in attachments:
            try:
                if fname.lower().endswith('.txt'):
                    report_string = fbytes.decode('utf-8')
                    txt_part = MIMEText(report_string, 'plain', 'utf-8')
                    txt_part.add_header('Content-Disposition', 'attachment', filename=fname)
                    msg.attach(txt_part)
                else:
                    maintype, subtype = (mimetypes.guess_type(fname)[0] or 'application/octet-stream').split('/')
                    msg.add_attachment(
                        fbytes,
                        maintype=maintype,
                        subtype=subtype,
                        filename=fname
                    )
            except Exception as e:
                logging.error(f"Falha ao anexar ficheiro {fname}: {e}")

    # Envio do E-mail
    try:
        smtp_class = smtplib.SMTP_SSL if USE_SMTP_SSL else smtplib.SMTP
        with smtp_class(SMTP_HOST, SMTP_PORT) as s:
            if not USE_SMTP_SSL: s.starttls()
            s.login(SMTP_USER, SMTP_PASS)
            s.send_message(msg)
    except Exception as e:
        logging.error(f"Falha ao enviar e-mail para {to_addr} (CC: {cc_addr}): {e}"); raise

# ====================================================================
# Função principal (LÓGICA DE E-MAIL CORRIGIDA)
# ====================================================================

def process_inbox():
    if not all([IMAP_HOST, IMAP_USER, IMAP_PASS, EMAIL_APROVADO_PARA, EMAIL_CC_GERAL]):
        logging.error("Configurações de IMAP ou E-mail (EMAIL_APROVADO_PARA / EMAIL_CC_GERAL) ausentes."); return
    imap = None
    try:
        imap = imaplib.IMAP4_SSL(IMAP_HOST)
        imap.login(IMAP_USER, IMAP_PASS)
        imap.select(f'"{IMAP_LABEL}"')
        status, data = imap.search(None, '(UNSEEN)')
        if status != "OK" or not data[0]:
            logging.info("Nenhum e-mail não lido encontrado."); return

        email_ids = data[0].split()
        logging.info(f"Encontrados {len(email_ids)} e-mails não lidos para verificar.")

        for num in email_ids:
            try:
                # Usar BODY.PEEK[] para não marcar como lido ('\Seen') automaticamente
                res, raw_full = imap.fetch(num, '(BODY.PEEK[])') 
                if res != 'OK': continue

                msg = email.message_from_bytes(raw_full[0][1])
                subject = clean_header(msg.get("Subject", ""))

                if ASSUNTO_PROVA_PALAVRA_CHAVE.lower() not in subject.lower():
                    logging.info(f"Ignorando e-mail (assunto '{subject}' não é de enunciado). Permanece não lido.")
                    continue

                logging.info(f"E-mail de enunciado encontrado: '{subject}'. A processar...")

                from_header = clean_header(msg.get("From", ""))
                sender_name, sender_email = parseaddr(from_header)
                if not sender_email:
                    sender_email = from_header
                    sender_name = "Remetente Desconhecido"

                docx_parts = []
                for part in msg.walk():
                    filename = clean_header(part.get_filename())
                    if filename and filename.lower().endswith(".docx"):
                        # Guarda o NOME (fname) e os BYTES (file_bytes)
                        docx_parts.append((filename, part.get_payload(decode=True)))

                if not docx_parts:
                    logging.warning(f"E-mail de enunciado de '{from_header}' não continha anexo .docx.")
                    # Marcar como lido para não processar novamente
                    imap.store(num, '+FLAGS', '\\Seen')
                    continue

                # Itera sobre todos os .docx encontrados no e-mail
                for fname, file_bytes in docx_parts:
                    text = extract_full_docx_text(file_bytes)
                    if not text:
                        logging.warning(f"Ficheiro {fname} está vazio ou ilegível.")
                        continue # Salta para o próximo anexo
                    
                    # 1. Verificação de Profanidade (Rígida)
                    found_profanities = check_for_profanity(text, LISTA_PALAVRAS_INAPROPRIADAS)
                    is_permissible_profanidade = len(found_profanities) == 0

                    # 2. Verificação Ortográfica
                    issues = spelling_issues_pt(text, PALAVRAS_PERSONALIZADAS)
                    is_permissible_ortografia = len(issues) <= MAX_ISSUES

                    # 3. Verificação de Conteúdo (IA)
                    disciplina, is_permissible_ia, justification, suggestions = evaluate_proof_content(text, DISCIPLINA_FALLBACK)

                    # 4. Decisão Final
                    final_is_permissible = is_permissible_ia and is_permissible_ortografia and is_permissible_profanidade

                    # 5. Relatório (Sempre criado)
                    report_content = (
                        f"RELATÓRIO DE VALIDAÇÃO AUTOMÁTICA\n"
                        f"====================================\n"
                        f"- Remetente: {from_header}\n"
                        f"- Ficheiro: {fname}\n"
                        f"- Disciplina Identificada (IA): {disciplina}\n"
                        f"---\n"
                        f"ANÁLISE DE LINGUAGEM IMPRÓPRIA (FILTRO)\n"
                        f"- Palavras Encontradas: {', '.join(found_profanities) if not is_permissible_profanidade else 'Nenhuma'}\n"
                        f"- Status Linguagem: {'Aceitável' if is_permissible_profanidade else 'REJEITADO (Linguagem Imprópria)'}\n"
                        f"---\n"
                        f"ANÁLISE ORTOGRÁFICA\n"
                        f"- Problemas Encontrados: {len(issues)} (Tolerância Máxima: {MAX_ISSUES})\n"
                        f"- Status Ortografia: {'Aceitável' if is_permissible_ortografia else 'Requer Atenção'}\n"
                        f"---\n"
                        f"ANÁLISE DE CONTEÚDO (IA)\n"
                        f"- Decisão IA: {'Permissível' if is_permissible_ia else 'Requer Revisão'}\n"
                        f"- Justificativa: {justification}\n"
                        f"- Sugestões: {suggestions}\n"
                        f"---\n"
                        f"DECISÃO FINAL: {'PERMISSÍVEL' if final_is_permissible else 'REQUER REVISÃO'}"
                    )

                    # Anexo do Relatório (para o remetente)
                    report_attachment = ("relatorio_validacao.txt", report_content.encode('utf-8'))
                    
                    # Anexo do ficheiro ORIGINAL (para o remetente)
                    original_doc_attachment = (fname, file_bytes)


                    common_email_args = {
                        "assinatura": ASSINATURA,
                        # Passa o caminho ABSOLUTO para a função de e-mail
                        "sign_image_path": SIGN_IMAGE_PATH, 
                        "sign_image_width_cm": SIGN_IMAGE_WIDTH_CM
                    }

                    # ==================================
                    # FLUXO DE APROVAÇÃO (PERMISSÍVEL)
                    # ==================================
                    if final_is_permissible:
                        
                        logging.info(f"Documento '{fname}' permissível. A tentar adicionar assinatura ao rodapé...")
                        
                        # 1. TENTAR ASSINAR
                        # Passa o caminho ABSOLUTO para a função de assinatura
                        modified_doc_bytes = add_signature_to_doc(file_bytes, SIGN_IMAGE_PATH, SIGN_IMAGE_WIDTH_CM)

                        # 2. VERIFICAR SE A ASSINATURA FALHOU
                        if not modified_doc_bytes:
                            # FALHA CRÍTICA. Abortar aprovação e tratar como REJEIÇÃO.
                            logging.error(f"FALHA CRÍTICA: Não foi possível adicionar a assinatura (imagem '{SIGN_IMAGE_PATH}' não encontrada?). O documento será REJEITADO.")
                            
                            # Construir um novo corpo de e-mail de erro para o remetente
                            attachments_rejeitado = [original_doc_attachment, report_attachment]
                            subject_out = f"[REVISÃO NECESSÁRIA] Enunciado: {disciplina} (Falha na Assinatura)"
                            body_out = (
                                f"Prezado(a) Professor(a) ({sender_name or sender_email}),\n\n"
                                f"O seu enunciado para '{disciplina}' foi APROVADO pela IA, mas foi REJEITADO no passo final.\n\n"
                                f"MOTIVO: O sistema não conseguiu aplicar a assinatura de validação (Ficheiro de imagem de assinatura em falta no servidor).\n\n"
                                f"Por favor, contacte o administrador ({EMAIL_CC_GERAL}). O seu enunciado original e o relatório da IA seguem em anexo."
                            )
                            send_email(
                                to_addr=sender_email,
                                cc_addr=EMAIL_CC_GERAL,
                                subject=subject_out,
                                body=body_out,
                                attachments=attachments_rejeitado,
                                **common_email_args
                            )
                            
                            # Salta para o próximo anexo
                            continue 
                        
                        # 3. SE CHEGOU AQUI, a assinatura (modified_doc_bytes) foi BEM SUCEDIDA.
                        # Continuar com a conversão para PDF...
                        
                        doc_attachment_final = None
                        logging.info("Assinatura adicionada. A tentar converter para PDF via LibreOffice...")
                        try:
                            pdf_bytes = convert_docx_to_pdf_linux(modified_doc_bytes) # Usa os bytes assinados
                            logging.info("Conversão para PDF bem-sucedida.")
                            pdf_fname = Path(fname).with_suffix('.pdf').name
                            doc_attachment_final = (f"ASSINADO_{pdf_fname}", pdf_bytes)
                        except Exception as e:
                            logging.error(f"Falha ao converter DOCX para PDF com LibreOffice: {e}. A enviar o DOCX assinado como fallback.")
                            doc_attachment_final = (f"ASSINADO_{fname}", modified_doc_bytes) # Usa os bytes assinados
                        
                        # ----- E-MAIL 1: Para Direção Académica (EMAIL_APROVADO_PARA) -----
                        # Envia APENAS o documento final assinado (doc_attachment_final)
                        
                        attachments_direcao = [doc_attachment_final]
                        
                        logging.info(f"Análise PERMISSÍVEL. A enviar para {EMAIL_APROVADO_PARA} (CC: {EMAIL_CC_GERAL}).")
                        subject_direcao = f"[APROVADO] Enunciado: {disciplina}"
                        
                        body_direcao = (
                            f"Prezado(a) Direção,\n\n"
                            f"O enunciado de prova submetido por '{from_header}' para a disciplina '{disciplina}' foi APROVADO.\n\n"
                            f"Segue em anexo o documento final, validado e assinado."
                        )
                        send_email(
                            to_addr=EMAIL_APROVADO_PARA,
                            cc_addr=EMAIL_CC_GERAL, # Coordenador é copiado
                            subject=subject_direcao,
                            body=body_direcao,
                            attachments=attachments_direcao,
                            **common_email_args
                        )

                        # ----- E-MAIL 2: Para Remetente (Professor) -----
                        # Envia o Relatório de Sucesso + O seu ficheiro Original.
                        
                        attachments_remetente = [original_doc_attachment, report_attachment] 
                        
                        logging.info(f"A enviar notificação de SUCESSO ao remetente original {sender_email}.")
                        subject_remetente = f"[APROVADO] O seu enunciado: {disciplina}"
                        body_remetente = (
                            f"Prezado(a) Professor(a) ({sender_name or sender_email}),\n\n"
                            f"Boas notícias! O enunciado de prova que submeteu para a disciplina '{disciplina}' foi APROVADO pelo sistema.\n\n"
                            f"O documento foi assinado e encaminhado para a Direção Académica.\n\n"
                            f"Para seu registo, seguem em anexo o relatório de validação e o seu ficheiro original submetido."
                        )
                        
                        send_email(
                            to_addr=sender_email, 
                            cc_addr=EMAIL_CC_GERAL, # Coordenador é copiado
                            subject=subject_remetente,
                            body=body_remetente,
                            attachments=attachments_remetente,
                            **common_email_args
                        )

                    # ==================================
                    # FLUXO DE REJEIÇÃO (REQUER REVISÃO)
                    # ==================================
                    elif REPLY_SENDER_ON_ERROR_FLAG:
                        
                        # ----- E-MAIL 1: Para Remetente (Professor) -----
                        # Envia o Relatório de Rejeição + Ficheiro Original.
                        
                        attachments_rejeitado = [original_doc_attachment, report_attachment]

                        logging.info(f"Análise REQUER REVISÃO. A devolver ao remetente {sender_email} (CC: {EMAIL_CC_GERAL}).")
                        subject_out = f"[REVISÃO NECESSÁRIA] Enunciado: {disciplina}"
                        body_out = (
                            f"Prezado(a) Professor(a) ({sender_name or sender_email}),\n\n"
                            f"O enunciado de prova que submeteu para a disciplina '{disciplina}' foi analisado e REQUER REVISÃO.\n\n"
                            f"O sistema identificou problemas: "
                            f"{'Linguagem imprópria detectada. ' if not is_permissible_profanidade else ''}"
                            f"{'Ortografia (limite de ' + str(MAX_ISSUES) + ' problemas excedido). ' if not is_permissible_ortografia else ''}"
                            f"{'Conteúdo (conforme decisão da IA). ' if not is_permissible_ia else ''}"
                            f"\n\nPor favor, consulte o relatório E o seu documento original em anexo para detalhes.\n\n"
                            f"Após corrigir, por favor, submeta novamente."
                        )
                        send_email(
                            to_addr=sender_email,
                            cc_addr=EMAIL_CC_GERAL, # Coordenador é copiado
                            subject=subject_out,
                            body=body_out,
                            attachments=attachments_rejeitado,
                            **common_email_args
                        )
                    else:
                        logging.warning(f"Análise REQUER REVISÃO, mas REPLY_SENDER_ON_ERROR está 'false'. Nenhuma devolução ao remetente será feita.")

                # Fim do loop 'for fname, file_bytes'
                
                # Marca o e-mail como lido apenas depois de processar todos os anexos
                imap.store(num, '+FLAGS', '\\Seen')
                logging.info(f"E-mail original de '{from_header}' foi processado e marcado como lido.")

            except Exception as e:
                logging.error(f"Erro ao processar e-mail ID {num.decode()}: {e}", exc_info=True)
                # Tenta marcar como lido mesmo em caso de erro de processamento, para não bloquear a fila
                try: imap.store(num, '+FLAGS', '\\Seen')
                except Exception: pass
                
    finally:
        if imap:
            try: imap.close(); imap.logout()
            except Exception: pass

def main():
    try:
        process_inbox()
    except Exception as e:
        logging.critical(f"Erro fatal na execução principal: {e}", exc_info=True)
        sys.exit(1)

if __name__ == "__main__":
    logging.info("Iniciando serviço de validação de enunciados.")
    main()
    logging.info("Serviço de validação finalizado.")
