# /frontend/pages/chatbot.py
import dash
from dash import html, dcc, callback, Input, Output, State, no_update, ctx
import dash_bootstrap_components as dbc
import requests
import uuid
import json
import random
from dash.exceptions import PreventUpdate

from config import CHATBOT_API_URL, CHATBOT_API_TOKEN, QUESTIONS, DOCS_USERNAME, DOCS_PASSWORD

# Registra a página
dash.register_page(__name__, path='/chatbot', name='Chatbot', order=1)

# --- Funções de API e Constantes ---
HEADERS = {"Authorization": f"Bearer {CHATBOT_API_TOKEN}"}
USE_AUTHENTICATION = False

WAIT_MESSAGES = [
    "Aguarde um momento enquanto processo as informações.", "Só mais um instante enquanto verifico.",
    "Aguarde só mais um pouquinho.", "Já estou verificando as informações...",
    "Estou consultando os dados para te responder.", "Verificando as informações, só um instante.",
    "Estou buscando a melhor resposta para você.", "Processando sua solicitação, por favor aguarde.",
    "Analisando sua pergunta, já te respondo.", "Consultando o sistema, aguarde um momento."
]

# UPDATED: Melhoria no tratamento de erro do carregamento de histórico
def load_history_from_api(session_id):
    try:
        res = requests.get(f"{CHATBOT_API_URL}/sessions/{session_id}/history", headers=HEADERS)
        res.raise_for_status()
        api_history = res.json()
        reconstructed_history = []
        for entry in api_history:
            reconstructed_history.append({"role": "user", "content": entry["user_input"]})
            reconstructed_history.append({"role": "agent", "content": entry["agent_response"]})
        return reconstructed_history
    except requests.exceptions.RequestException as e:
        # Em vez de retornar [], retorna uma mensagem de erro para ser exibida na UI
        return [{"role": "system", "content": f"❌ Erro de conexão ao carregar o histórico. A API pode estar indisponível."}]
    except Exception as e:
        return [{"role": "system", "content": f"❌ Ocorreu um erro inesperado ao carregar o histórico: {e}"}]

# UPDATED: Melhoria no tratamento de erro do reset de sessão
def reset_session_api(session_id):
    """Retorna uma tupla (sucesso, mensagem)."""
    try:
        res = requests.post(
            f"{CHATBOT_API_URL}/reset-session",
            headers=HEADERS,
            params={"session_id": session_id}
        )
        res.raise_for_status()
        return (True, "Sessão reiniciada com sucesso!")
    except requests.exceptions.RequestException as e:
        return (False, f"Erro de conexão ao reiniciar a sessão: {e}")
    except Exception as e:
        return (False, f"Erro inesperado ao reiniciar a sessão: {e}")

def _parse_search(search: str) -> dict:
    if not search:
        return {}
    try:
        return dict(x.split('=', 1) for x in search.strip('?').split('&') if '=' in x)
    except Exception:
        return {}

def layout(**kwargs):
    """
    Aceita **kwargs porque o Dash Pages pode injetar parâmetros (ex.: session_id) no layout.
    Aqui mantemos a leitura oficial via _pages_location.search nos callbacks.
    """
    return html.Div(
        className="chat-area-container",
        children=[
            html.H2("Assistente Virtual"),
            
            # UPDATED: Container para exibir alertas (ex: sucesso/falha ao limpar histórico)
            html.Div(id="chatbot-alert-container", className="my-2"),

            # Timer para "mensagens de espera" (desligado por padrão)
            dcc.Interval(id="wait-ui-timer", interval=6000, n_intervals=0, disabled=True),

            # HISTÓRICO (único scroller)
            html.Div(
                className="chat-conversation-container",
                children=dcc.Loading(
                    id="loading-chat",
                    type="dot",
                    parent_style={"height": "auto"},
                    children=html.Div(
                        id="chat-conversation",
                        className="border rounded bg-white p-3"
                    )
                )
            ),

            # INPUT (irmão do histórico, sempre visível acima do rodapé)
            html.Div(
                className="chat-input-bar",
                children=dbc.InputGroup([
                    dbc.Input(id="chat-input", placeholder="Sua mensagem...", n_submit=0),
                    dbc.Button("Enviar", id="send-button", n_clicks=0, color="danger")
                ])
            )
        ]
    )

# --- Callbacks ---

# 1) Autenticação (usa prop 'invalid', não 'is_invalid')
@callback(
    Output('auth-store', 'data'),
    Output('auth-modal', 'is_open', allow_duplicate=True),
    Output('password-input', 'invalid'),
    Input('password-submit', 'n_clicks'),
    State('password-input', 'value'),
    State('username-input', 'value'),
    prevent_initial_call=True,
)
def handle_authentication(n_clicks, password, username):
    if password == DOCS_PASSWORD and username == DOCS_USERNAME:
        return {'authenticated': True}, False, False
    return no_update, True, True

# 2) Inicializa a sessão de acordo com a URL:
#    - /chatbot?session_id=XYZ  -> carrega histórico da API para XYZ
#    - /chatbot (sem query)     -> cria novo session_id e zera histórico
@callback(
    Output('session-id-store', 'data'),
    Output('chat-history-store', 'data'),
    Output('auth-modal', 'is_open'),
    Input('_pages_location', 'pathname'),
    Input('_pages_location', 'search'),
    State('auth-store', 'data'),
    State('session-id-store', 'data'),
    prevent_initial_call=False,
)
def initialize_session(pathname, search, auth_data, current_session_id):
    # Só opera no /chatbot
    if pathname != "/chatbot":
        raise PreventUpdate

    # (Se ativar auth no chat) bloqueia até autenticar
    if USE_AUTHENTICATION and not (auth_data and auth_data.get('authenticated')):
        return no_update, no_update, True

    params = _parse_search(search)
    url_sid = params.get('session_id')

    if url_sid:
        # Chegou via "Abrir no Chat" (ou URL compartilhada) -> carrega esse histórico
        if current_session_id == url_sid:
            # Já está sincronizado; nada a fazer
            return no_update, no_update, False
        history = load_history_from_api(url_sid)
        return url_sid, history, False

    # Sem session_id na URL -> sempre gera conversa nova
    new_session_id = str(uuid.uuid4())
    return new_session_id, [], False

# 3) **Sincroniza a URL** com o session_id atual (sem recarregar a página).
@callback(
    Output('router', 'search'),
    Input('session-id-store', 'data'),
    Input('_pages_location', 'pathname'),
    State('_pages_location', 'search'),
    prevent_initial_call=True
)
def sync_session_id_to_url(session_id, pathname, current_search):
    if pathname != "/chatbot" or not session_id:
        raise PreventUpdate
    expected = f"?session_id={session_id}"
    if current_search == expected:
        raise PreventUpdate
    return expected

# 4) Limpa Stores quando SAIR do Chatbot (garante fresh start ao voltar sem query)
@callback(
    Output('chat-history-store', 'data', allow_duplicate=True),
    Output('session-id-store', 'data', allow_duplicate=True),
    Input('_pages_location', 'pathname'),
    prevent_initial_call=True
)
def clear_chat_stores_on_leave(pathname):
    if pathname != "/chatbot":
        # limpamos histórico e session_id ao sair
        return [], None
    raise PreventUpdate

# 5) Controla o Interval de "aguarde": liga quando esperando, desliga após resposta
@callback(
    Output('wait-ui-timer', 'disabled'),
    Output('wait-ui-timer', 'n_intervals'),
    Input('chat-history-store', 'data'),
    prevent_initial_call=False
)
def control_wait_interval(history):
    if not history:
        return True, 0
    waiting = history[-1]['role'] == 'user'
    if waiting:
        return False, 0
    return True, no_update

# 6) Input do usuário (envio/enter e exemplos)
@callback(
    Output('chat-history-store', 'data', allow_duplicate=True),
    Output('chat-input', 'value'),
    Input('send-button', 'n_clicks'),
    Input('chat-input', 'n_submit'),
    Input({"type": "question-btn", "index": dash.ALL}, "n_clicks"),
    State('chat-input', 'value'),
    State('chat-history-store', 'data'),
    State('auto-send-store', 'data'),   # <-- lê do Store, não do checkbox
    prevent_initial_call=True
)
def handle_user_input(send_clicks, enter_submit, q_clicks, user_input, history, auto_send):
    # fallback robusto
    auto_send = True if auto_send is None else bool(auto_send)

    triggered_id = ctx.triggered_id

    if triggered_id in ['send-button', 'chat-input'] and user_input:
        history.append({"role": "user", "content": user_input})
        return history, ""

    if isinstance(triggered_id, dict) and triggered_id.get("type") == "question-btn":
        if any(q_clicks):
            idx = triggered_id['index']
            question_text = QUESTIONS[idx]
            if auto_send:
                history.append({"role": "user", "content": question_text})
                return history, ""
            else:
                return no_update, question_text

    return no_update, no_update

# 7) Busca resposta na API
@callback(
    Output('chat-history-store', 'data', allow_duplicate=True),
    Input('chat-history-store', 'data'),
    State('session-id-store', 'data'),
    prevent_initial_call=True
)
def fetch_chatbot_response(history, session_id):
    if not history or history[-1]['role'] != 'user':
        return no_update

    user_input = history[-1]['content']
    payload = {
        "message": user_input,
        "history": history[:-1],
        "session_id": session_id
    }

    try:
        response = requests.post(f'{CHATBOT_API_URL}/chat', json=payload, headers=HEADERS, timeout=60)
        response.raise_for_status()
        data = response.json()
        agent_response = data.get("response") or data.get("last_message", "🕓 Desculpe, não obtive uma resposta.")
    except requests.exceptions.RequestException as e:
        agent_response = f"❌ Erro de conexão ao tentar contactar a API: {str(e)}"
    except Exception as e:
        agent_response = f"❌ Ocorreu um erro inesperado: {str(e)}"

    history.append({"role": "agent", "content": agent_response})
    return history

# 8) Limpa histórico (gera nova sessão) → URL será atualizada pelo callback #3
@callback(
    Output('chat-history-store', 'data', allow_duplicate=True),
    Output('session-id-store', 'data', allow_duplicate=True),
    Output('confirm-reset-alert', 'is_open'),
    Input('clear-history-btn', 'n_clicks'),
    State('session-id-store', 'data'),
    prevent_initial_call=True
)
def clear_history(n_clicks, session_id):
    if n_clicks and session_id:
        if reset_session_api(session_id):
            new_session_id = str(uuid.uuid4())
            return [], new_session_id, True
    return no_update, no_update, False

# 9) Renderiza a conversa (cicla as mensagens de espera quando Interval está habilitado)
@callback(
    Output('chat-conversation', 'children'),
    Input('chat-history-store', 'data'),
    Input('wait-ui-timer', 'n_intervals')
)
def update_conversation_display(history, n_intervals):
    if not history:
        return [html.P("Olá! Como posso ajudar hoje?")]

    convo = []
    waiting = history[-1]['role'] == 'user'

    for msg in history:
        if msg['role'] == 'user':
            convo.append(html.P(f"Você: {msg['content']}", className="user-message"))
        elif msg['role'] == 'agent':
            try:
                content = json.loads(msg["content"])
                text = content.get("text", msg["content"])
            except (json.JSONDecodeError, TypeError):
                text = msg["content"]

            convo.append(html.Div([
                html.P("Agente:", className="agent-message-header"),
                dcc.Markdown(text, dangerously_allow_html=False, link_target="_blank", className="agent-message")
            ], className="agent-message"))

    if waiting:
        idx = 0 if n_intervals is None else (n_intervals % len(WAIT_MESSAGES))
        wait_text = WAIT_MESSAGES[idx]
        convo.append(
            html.Div([
                html.P("Agente:", className="agent-message-header"),
                html.Div([
                    dbc.Spinner(size="sm", spinnerClassName="me-2"),
                    html.Span(wait_text)
                ])
            ], className="agent-message")
        )

    return convo
