# -*- coding: utf-8 -*-
"""
Automacao inicial SSW - RCL - programa 455.

Etapa atual:
- acessa o SSW;
- faz login;
- mantem a filial padrao do usuario;
- informa o programa 455 no menu;
- aguarda abrir a tela do relatorio.

As proximas etapas entram depois:
- preencher data inicial/final;
- informar saida E de Excel;
- solicitar o relatorio;
- acessar a tela de relatorios gerados;
- baixar no servidor;
- acionar o importador.
"""

import os
import json
import shutil
import subprocess
import time
import calendar
from datetime import date
from datetime import datetime, timedelta, timezone
from email.message import EmailMessage
import smtplib

from selenium import webdriver
from selenium.common.exceptions import NoAlertPresentException, TimeoutException
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait
from webdriver_manager.chrome import ChromeDriverManager


# True = abre Chrome visivel para teste local.
# False = modo servidor/cron, headless.
DEBUG = True
EMAIL_ALERTA_ATIVO = True
EMAIL_DESTINATARIO = "suporte@kreativesistemas.com.br"
EMAIL_REMETENTE = "suporte@kreativesistemas.com.br"

# Se SMTP_HOST ficar vazio, no servidor Linux o script tenta usar sendmail.
SMTP_HOST = ""
SMTP_PORT = 587
SMTP_USUARIO = ""
SMTP_SENHA = ""
SMTP_USAR_TLS = True
TIMEZONE = timezone(timedelta(hours=-3))

# ==============================
# CONFIGURACOES DE LOGIN SSW - RCL
# ==============================

DOMINIO = "RC5"
CPF = "33775385002"
USUARIO = "kreative"
SENHA = "26200209"
RELATORIO_TITULO = "455 - Fretes Expedidos/Recebidos - CTRCs"

# ==============================
# CONFIGURACOES DO SSW
# ==============================

PROGRAMA = "455"
URL_LOGIN = "https://sistema.ssw.inf.br/bin/ssw0422"
URL_MENU = "https://sistema.ssw.inf.br/bin/menu01"


def enviar_alerta_email(etapa, erro, extra=""):
    if not EMAIL_ALERTA_ATIVO:
        return

    assunto = f"[SSW-RCL] Falha relatorio 455 - {etapa}"
    corpo = f"""Falha na automacao do relatorio 455 - RCL.

Etapa: {etapa}
Data/hora: {datetime.now(TIMEZONE).strftime('%Y-%m-%d %H:%M:%S')}

Erro:
{erro}

Detalhes:
{extra}
"""

    mensagem = EmailMessage()
    mensagem["From"] = EMAIL_REMETENTE
    mensagem["To"] = EMAIL_DESTINATARIO
    mensagem["Subject"] = assunto
    mensagem.set_content(corpo)

    try:
        if SMTP_HOST:
            with smtplib.SMTP(SMTP_HOST, SMTP_PORT, timeout=30) as smtp:
                if SMTP_USAR_TLS:
                    smtp.starttls()
                if SMTP_USUARIO:
                    smtp.login(SMTP_USUARIO, SMTP_SENHA)
                smtp.send_message(mensagem)
        else:
            php_bin = os.getenv("PHP_BIN") or shutil.which("php")

            if php_bin:
                payload = {
                    "to": EMAIL_DESTINATARIO,
                    "subject": assunto,
                    "body": corpo,
                    "from": EMAIL_REMETENTE,
                }
                php_code = r'''
$payload = json_decode(stream_get_contents(STDIN), true);
$from = $payload["from"];
$headers = "From:" . $from . "\r\n" .
    "Reply-To:" . $from . "\r\n" .
    "Content-type:text/plain;charset=UTF-8\r\n";

if (!mail($payload["to"], $payload["subject"], $payload["body"], $headers)) {
    fwrite(STDERR, "mail() retornou false");
    exit(1);
}
'''
                subprocess.run(
                    [php_bin, "-r", php_code],
                    input=json.dumps(payload),
                    text=True,
                    capture_output=True,
                    check=True,
                    timeout=30,
                )
            elif os.name == "nt":
                print("Ambiente Windows sem PHP/SMTP configurado; alerta por email nao enviado.")
                print(corpo)
                return
            else:
                sendmail = shutil.which("sendmail") or "/usr/sbin/sendmail"
                subprocess.run(
                    [sendmail, "-t", "-oi"],
                    input=mensagem.as_bytes(),
                    check=True,
                    timeout=30,
                )

        print(f"Alerta enviado para {EMAIL_DESTINATARIO}.")
    except Exception as exc:
        print(f"Nao foi possivel enviar alerta por email: {exc}")


def periodo_mes_atual_ssw():
    hoje = date.today()
    data_inicial = hoje.replace(day=1)
    ultimo_dia = calendar.monthrange(hoje.year, hoje.month)[1]
    data_final = hoje.replace(day=ultimo_dia)

    return data_inicial.strftime("%d%m%y"), data_final.strftime("%d%m%y")


def criar_driver():
    options = Options()

    if DEBUG:
        options.add_experimental_option("detach", True)
    else:
        options.add_argument("--headless=new")

    options.add_argument("--window-size=1200,800")
    options.add_argument("--disable-gpu")
    options.add_argument("--disable-extensions")
    options.add_argument("--disable-notifications")
    options.add_argument("--no-sandbox")
    options.add_argument("--disable-setuid-sandbox")
    options.add_argument("--disable-dev-shm-usage")

    chrome_bin = os.getenv("CHROME_BINARY")
    if chrome_bin:
        options.binary_location = chrome_bin
    elif not DEBUG:
        for candidato in ("google-chrome", "google-chrome-stable", "chromium", "chromium-browser"):
            caminho = shutil.which(candidato)
            if caminho:
                options.binary_location = caminho
                break

    chromedriver_path = os.getenv("CHROMEDRIVER_PATH")
    if not chromedriver_path and not DEBUG:
        for candidato in ("/usr/bin/chromedriver", "/usr/local/bin/chromedriver"):
            if os.path.exists(candidato):
                chromedriver_path = candidato
                break

    service = Service(chromedriver_path) if chromedriver_path else Service(ChromeDriverManager().install())

    return webdriver.Chrome(service=service, options=options)


def aceitar_alerta_se_existir(driver):
    try:
        alerta = driver.switch_to.alert
        texto = alerta.text
        print("ALERTA ENCONTRADO:")
        print(texto)
        alerta.accept()
        print("Alerta fechado automaticamente.")
        return texto
    except NoAlertPresentException:
        return None


def clicar_modal_abrir_opcao_156(driver, timeout=20):
    fim = time.time() + timeout

    while time.time() < fim:
        resultado = driver.execute_script("""
            const box = document.getElementById('errormsg');
            if (!box || box.style.visibility === 'hidden') {
                return null;
            }

            const texto = (box.innerText || '').replace(/\\s+/g, ' ').trim();
            const links = Array.from(box.querySelectorAll('a.dialog'));
            const opcao156 = links.find((el) =>
                (el.textContent || '').includes('Abrir a opção 156 agora') ||
                (el.textContent || '').includes('Abrir a opcao 156 agora') ||
                el.id === '-1'
            );

            if (!opcao156) {
                return {encontrado: true, clicado: false, texto};
            }

            opcao156.click();
            return {encontrado: true, clicado: true, texto};
        """)

        if resultado and resultado.get("encontrado"):
            print("Modal de solicitacao encontrado:")
            print(resultado.get("texto", ""))

            if resultado.get("clicado"):
                print("Opcao 'Abrir a opcao 156 agora' selecionada.")
                time.sleep(5)
                aceitar_alerta_se_existir(driver)
                return True

            texto = resultado.get("texto", "")
            erro = "Modal encontrado, mas a opcao 156 nao foi localizada."
            print(erro)
            enviar_alerta_email("Abrir opcao 156", erro, texto)
            raise RuntimeError(erro)

        time.sleep(1)

    erro = "Modal de solicitacao do relatorio nao apareceu no tempo esperado."
    print(erro)
    enviar_alerta_email("Abrir opcao 156", erro)
    raise RuntimeError(erro)


def data_hoje_fila_ssw():
    return date.today().strftime("%d/%m/%y")


def clicar_atualizar_fila(driver):
    try:
        resultado = driver.execute_script("""
            if (typeof ajaxEnvia === 'function') {
                ajaxEnvia('', 0);
                return 'ajaxEnvia';
            }

            const links = Array.from(document.querySelectorAll('a'));
            const atualizar = links.find((link) =>
                link.id === '2' ||
                (link.innerText || '').replace(/\\s+/g, ' ').trim().toLowerCase() === 'atualizar'
            );

            if (!atualizar) {
                return 'nao_encontrado';
            }

            atualizar.click();
            return 'click';
        """)

        if resultado == "nao_encontrado":
            print("Botao Atualizar nao localizado na fila 156.")
            return False

        print(f"Fila atualizada via {resultado}.")
        time.sleep(8)
        aceitar_alerta_se_existir(driver)
        return True
    except Exception as exc:
        print(f"Nao foi possivel atualizar a fila 156: {exc}")
        return False


def baixar_relatorio_455_na_fila(driver, timeout=90):
    print("Aguardando processamento inicial da fila 156...")
    time.sleep(10)

    data_hoje = data_hoje_fila_ssw()
    fim = time.time() + timeout
    ultima_situacao = ""

    while time.time() < fim:
        clicar_atualizar_fila(driver)

        resultado = driver.execute_script("""
            const normalizar = (valor) => String(valor || '')
                .normalize('NFD')
                .replace(/[\\u0300-\\u036f]/g, '')
                .toLowerCase();

            const dataHoje = arguments[0];
            const usuario = normalizar(arguments[1]);
            const titulo = arguments[2];

            const linhas = Array.from(document.querySelectorAll('tr'));
            const candidatos = [];

            for (const linha of linhas) {
                const textoLinha = (linha.innerText || '').replace(/\\s+/g, ' ').trim();
                const textoNormalizado = normalizar(textoLinha);
                const linkBaixar = Array.from(linha.querySelectorAll('a')).find((a) =>
                    normalizar(a.innerText).trim() === 'baixar'
                );

                if (
                    textoLinha.includes(titulo) &&
                    textoNormalizado.includes(usuario) &&
                    textoLinha.includes(dataHoje)
                ) {
                    const sequenciaMatch = textoLinha.match(/^\\s*(\\d+)/);
                    const dataHoraMatch = textoLinha.match(/\\b\\d{2}\\/\\d{2}\\/\\d{2}\\s+\\d{2}:\\d{2}:\\d{2}\\b/);
                    const situacaoMatch = textoLinha.match(/\\b(Conclu[ií]do|Em processamento|Processando|Erro)\\b/i);

                    candidatos.push({
                        sequencia: sequenciaMatch ? sequenciaMatch[1] : '0',
                        dataHora: dataHoraMatch ? dataHoraMatch[0] : '',
                        situacao: situacaoMatch ? situacaoMatch[1] : '',
                        temBaixar: Boolean(linkBaixar),
                        linkBaixar,
                        texto: textoLinha
                    });
                }
            }

            candidatos.sort((a, b) => Number(b.sequencia) - Number(a.sequencia));
            const escolhido = candidatos[0] || null;

            if (!escolhido) {
                const linksDownload = Array.from(document.querySelectorAll('a')).map((link) => {
                    const texto = normalizar(link.innerText).trim();
                    const onclick = String(link.getAttribute('onclick') || '');
                    const match = onclick.match(/DOW(\\d+)/i);

                    if (texto !== 'baixar' || !match) {
                        return null;
                    }

                    return {
                        sequencia: match[1],
                        dataHora: '',
                        situacao: 'Concluido',
                        temBaixar: true,
                        linkBaixar: link,
                        texto: link.closest('tr') ? (link.closest('tr').innerText || '') : onclick,
                        fallback: true
                    };
                }).filter(Boolean);

                linksDownload.sort((a, b) => Number(b.sequencia) - Number(a.sequencia));
                const downloadMaisRecente = linksDownload[0] || null;

                if (!downloadMaisRecente) {
                    return {
                        encontrado: false,
                        motivo: 'Relatorio nao localizado na fila.',
                        debug: {
                            linhas: linhas.length,
                            linksBaixar: Array.from(document.querySelectorAll('a')).filter((a) =>
                                normalizar(a.innerText).trim() === 'baixar'
                            ).length
                        }
                    };
                }

                downloadMaisRecente.linkBaixar.click();

                return {
                    encontrado: true,
                    sequencia: downloadMaisRecente.sequencia,
                    dataHora: downloadMaisRecente.dataHora,
                    situacao: downloadMaisRecente.situacao,
                    temBaixar: true,
                    clicado: true,
                    fallback: true,
                    texto: downloadMaisRecente.texto
                };
            }

            const retorno = {
                encontrado: true,
                sequencia: escolhido.sequencia,
                dataHora: escolhido.dataHora,
                situacao: escolhido.situacao,
                temBaixar: escolhido.temBaixar,
                texto: escolhido.texto
            };

            if (normalizar(escolhido.situacao).includes('concluido') && escolhido.temBaixar) {
                escolhido.linkBaixar.click();
                retorno.clicado = true;
            } else {
                retorno.clicado = false;
            }

            return retorno;
        """, data_hoje, USUARIO, RELATORIO_TITULO)

        if resultado and resultado.get("encontrado"):
            ultima_situacao = (
                f"Seq {resultado.get('sequencia')} | "
                f"{resultado.get('dataHora')} | "
                f"Situacao: {resultado.get('situacao')} | "
                f"Baixar: {resultado.get('temBaixar')}"
            )
            print("Relatorio localizado na fila:")
            print(ultima_situacao)
            if resultado.get("fallback"):
                print("Localizacao feita por fallback: maior link DOW/Baixar disponivel.")

            if resultado.get("clicado"):
                print("Link Baixar clicado.")
                time.sleep(8)
                aceitar_alerta_se_existir(driver)
                return True
        else:
            motivo = (resultado or {}).get("motivo", "Relatorio ainda nao localizado.")
            debug = (resultado or {}).get("debug")
            if debug:
                motivo += f" Debug: {debug}"
            print(motivo)

        time.sleep(6)

    erro = "Relatorio 455 nao ficou disponivel para download no tempo esperado."
    enviar_alerta_email("Download fila 156", erro, ultima_situacao)
    raise RuntimeError(erro)


def realizar_login(driver):
    print("Acessando tela de login...")
    driver.get(URL_LOGIN)
    time.sleep(5)

    print("Realizando login RCL...")
    driver.find_element(By.ID, "1").send_keys(DOMINIO)
    driver.find_element(By.ID, "2").send_keys(CPF)
    driver.find_element(By.ID, "3").send_keys(USUARIO)
    driver.find_element(By.ID, "4").send_keys(SENHA)
    driver.find_element(By.ID, "5").click()
    time.sleep(5)

    aceitar_alerta_se_existir(driver)
    print("Login efetuado.")


def abrir_programa_455(driver):
    print("Acessando menu principal...")
    driver.get(URL_MENU)
    time.sleep(3)

    janela_menu = driver.current_window_handle

    # Nao altera filial: usa a filial padrao carregada pelo usuario no SSW.
    try:
        campo_filial = driver.find_element(By.ID, "2")
        print("Filial padrao atual:", campo_filial.get_attribute("value"))
    except Exception:
        print("Campo filial nao localizado, seguindo sem alterar filial.")

    print(f"Informando programa: {PROGRAMA}")
    campo_programa = WebDriverWait(driver, 20).until(
        EC.presence_of_element_located((By.ID, "3"))
    )
    campo_programa.clear()
    campo_programa.send_keys(PROGRAMA)
    time.sleep(1)

    aceitar_alerta_se_existir(driver)

    print("Aguardando abertura da tela do programa 455...")
    try:
        WebDriverWait(driver, 20).until(lambda navegador: len(navegador.window_handles) > 1)

        for handle in driver.window_handles:
            if handle != janela_menu:
                driver.switch_to.window(handle)
                break
    except TimeoutException:
        print("Nenhuma nova janela abriu; verificando se a tela carregou na mesma janela.")

    time.sleep(3)
    aceitar_alerta_se_existir(driver)

    print("Tela atual:")
    print(driver.current_url)
    print(driver.title)

    return janela_menu, driver.current_window_handle


def solicitar_relatorio_455_excel(driver):
    data_inicial, data_final = periodo_mes_atual_ssw()

    print("Preenchendo periodo do relatorio 455...")
    print(f"Data inicial: {data_inicial}")
    print(f"Data final: {data_final}")

    campo_data_inicial = WebDriverWait(driver, 20).until(
        EC.presence_of_element_located((By.ID, "11"))
    )
    campo_data_final = driver.find_element(By.ID, "12")

    driver.execute_script("""
        const campo = arguments[0];
        campo.value = arguments[1];
        campo.dispatchEvent(new Event('input', { bubbles: true }));
        campo.dispatchEvent(new Event('change', { bubbles: true }));
    """, campo_data_inicial, data_inicial)

    driver.execute_script("""
        const campo = arguments[0];
        campo.value = arguments[1];
        campo.dispatchEvent(new Event('input', { bubbles: true }));
        campo.dispatchEvent(new Event('change', { bubbles: true }));
    """, campo_data_final, data_final)

    print("Selecionando saida Excel: E")
    campo_saida = driver.find_element(By.ID, "35")
    driver.execute_script("""
        const campo = arguments[0];
        campo.value = 'E';
        campo.dispatchEvent(new Event('input', { bubbles: true }));
        campo.dispatchEvent(new Event('change', { bubbles: true }));
    """, campo_saida)

    aceitar_alerta_se_existir(driver)

    print("Clicando para solicitar relatorio...")
    botao_avancar = driver.find_element(By.ID, "40")
    driver.execute_script("arguments[0].click();", botao_avancar)

    time.sleep(5)
    aceitar_alerta_se_existir(driver)

    clicar_modal_abrir_opcao_156(driver)

    print("Relatorio solicitado. Tela atual:")
    print(driver.current_url)
    print(driver.title)


def executar():
    driver = criar_driver()

    try:
        realizar_login(driver)
        abrir_programa_455(driver)
        solicitar_relatorio_455_excel(driver)
        baixar_relatorio_455_na_fila(driver)

        print("Automacao clicou no download do relatorio 455.")
        print("Confira se o arquivo baixou corretamente para seguirmos com a importacao.")

        if DEBUG:
            input("Pressione ENTER para encerrar o teste local...")

    finally:
        if not DEBUG:
            driver.quit()
        else:
            print("DEBUG ativo: Chrome mantido aberto para inspecao.")


if __name__ == "__main__":
    executar()
