"""Abre un chat de WhatsApp Web en una pestana ya logueada via Chrome DevTools Protocol.

Compone cuatro primitivas CDP del registry (`cdp_eval`, `cdp_type_chars`,
`cdp_press_key`, `cdp_click_xy`) para localizar y abrir un chat por su nombre
exacto SIN abrir ventana nueva ni darle foco al sistema:

  1. Limpia el buscador (`Escape` no basta: el texto se acumula -> select + Backspace).
  2. Enfoca el input de busqueda y teclea el nombre caracter a caracter.
  3. Localiza el chat por su ancla estable `span[title="<nombre exacto>"]` dentro
     de `#side` y calcula el centro de su bounding box.
  4. Hace un click de RATON REAL sobre esas coordenadas (un `element.click()` JS
     no abre el chat: los handlers de React lo ignoran).
  5. Verifica que abrio comprobando que el aria-label del composer contiene el nombre.

Base de `whatsapp_read_chat` y `whatsapp_send_message`: ambas necesitan el chat
abierto antes de leer o enviar.
"""

import json
import os
import sys
import time

sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

from browser.cdp_eval import cdp_eval
from browser.cdp_type_chars import cdp_type_chars
from browser.cdp_press_key import cdp_press_key
from browser.cdp_click_xy import cdp_click_xy


def _ev(expr: str, port: int, substr: str) -> dict:
    """Atajo: evalua una expresion JS en el target de WhatsApp."""
    return cdp_eval(expr, port=port, target_url_substr=substr)


def whatsapp_open_chat(
    name: str,
    *,
    port: int = 9222,
    target_url_substr: str = "whatsapp",
    wait_s: float = 1.3,
) -> dict:
    """Abre un chat de WhatsApp Web por su nombre exacto en una pestana logueada.

    Args:
        name: Nombre EXACTO del chat/grupo tal y como aparece en la lista lateral
            (match exacto del atributo `title` del `span` ancla). Nombres ambiguos
            abren el primero que matchee.
        port: Puerto de remote debugging de Chrome. Default 9222.
        target_url_substr: Substring que debe contener la URL del target (pestana).
            Default "whatsapp".
        wait_s: Segundos de espera tras teclear el nombre para que la lista lateral
            filtre y renderice los resultados. Default 1.3.

    Returns:
        dict con claves:
            opened: bool — True si el chat se abrio (el nombre aparece en el
                aria-label del composer).
            name: str — el nombre solicitado.
            composer_label: str — aria-label del composer (solo si abrio).
            reason: str — motivo del fallo (solo si no abrio).
            coords: dict {x, y} — coordenadas del click (solo si encontro el ancla).
    """
    substr = target_url_substr

    # 1. Limpiar el buscador. Escape NO basta: el texto se acumula entre llamadas,
    #    asi que seleccionamos todo el contenido del input y lo borramos.
    cdp_press_key("Escape", port=port, target_url_substr=substr)
    time.sleep(0.3)
    _ev(
        "var i=document.querySelector('#side input'); if(i){i.focus(); i.select();}",
        port,
        substr,
    )
    cdp_press_key("Backspace", port=port, target_url_substr=substr)
    time.sleep(0.2)

    # 2. Enfocar el input de busqueda y teclear el nombre caracter a caracter.
    _ev("var i=document.querySelector('#side input'); if(i){i.focus();}", port, substr)
    time.sleep(0.2)
    cdp_type_chars(name, port=port, target_url_substr=substr, delay_ms=15)
    time.sleep(wait_s)

    # 3. Localizar el ancla estable: span[title] con nombre EXACTO dentro de #side.
    #    Devuelve el centro del bounding box, o un marcador offscreen si no es visible.
    expr = (
        "(() => { const name=" + json.dumps(name) + ";"
        "const a=[...document.querySelectorAll('#side span[title]')]"
        ".find(s=>s.getAttribute('title')===name);"
        "if(!a) return null; const b=a.getBoundingClientRect();"
        "if(b.width===0||b.y<0) return JSON.stringify({offscreen:true});"
        "return JSON.stringify({x:Math.round(b.x+b.width/2),y:Math.round(b.y+b.height/2)});})()"
    )
    r = _ev(expr, port, substr)
    if not r.get("value"):
        return {
            "opened": False,
            "name": name,
            "reason": "chat no encontrado en la lista (no cargado o nombre inexacto)",
        }

    c = json.loads(r["value"])
    if c.get("offscreen"):
        return {
            "opened": False,
            "name": name,
            "reason": "chat fuera de viewport (scroll necesario)",
        }

    # 4. Click de raton real sobre el ancla. Un element.click() JS NO abre el chat
    #    porque los handlers de React no reaccionan a eventos sinteticos del DOM.
    cdp_click_xy(c["x"], c["y"], port=port, target_url_substr=substr)
    time.sleep(1.1)

    # 5. Verificar: el composer (footer contenteditable) apunta al chat abierto.
    chk = _ev(
        "var b=document.querySelector('footer div[contenteditable=\"true\"]'); "
        "b?b.getAttribute('aria-label'):null",
        port,
        substr,
    )
    label = chk.get("value") or ""
    return {
        "opened": name in label,
        "name": name,
        "composer_label": label,
        "coords": c,
    }


if __name__ == "__main__":
    chat = sys.argv[1] if len(sys.argv) > 1 else "NOTAS WASAP"
    out = whatsapp_open_chat(chat, port=9222, target_url_substr="whatsapp")
    print(json.dumps(out, ensure_ascii=False, indent=2))
