"""
Together Kit — utilidades de UX dos scripts do aluno (saida bonita + confirmacao).

Convencao "ZONA DE EDICAO": cada script de conteudo tem um bloco entre
    # >>> ZONA DE EDICAO (a IA preenche; voce revisa)
    ...
    # <<< FIM DA ZONA DE EDICAO
que a IA preenche a partir do prompt da aula. O aluno revisa o diff e roda
com Ctrl+F5. A regra de ouro: a IA propoe, voce decide.
"""
import subprocess
import sys
from idioma import T, Tf

try:
    sys.stdout.reconfigure(encoding='utf-8', errors='replace')
except Exception:
    pass


def header(title):
    bar = "=" * 60
    print(f"\n{bar}\n  {title}\n{bar}")


def step(msg):
    print(f"  - {msg}")


def ok(msg):
    print(f"  [OK] {msg}")


def warn(msg):
    print(f"  {T('[AVISO]')} {msg}")


def done(msg=None):
    # O padrao nasce dentro da funcao, e nao na assinatura: um default avaliado no import
    # congela o idioma antes de o .env ser lido.
    print(f"\n{msg if msg is not None else T('Pronto!')}")


def confirm(question):
    """Pergunta s/N. True se o aluno confirmar."""
    return input(f"{question} {T('[s/N]')} ").strip().lower() in ('s', 'sim', 'y', 'yes')


# Copiar/colar e trabalho operacional: nao gera valor nenhum e ainda erra (o aluno
# copia meio texto, esquece uma linha). Se o script ja tem o prompt pronto na mao,
# ele entrega o prompt PRONTO — na area de transferencia. O aluno so da Ctrl+V na IA.
_COPIADORES_UNIX = {
    "darwin": [["pbcopy"]],
    None: [["xclip", "-selection", "clipboard"], ["xsel", "--clipboard", "--input"]],
}


def _copiar_windows(texto):
    """Clipboard do Windows via API (CF_UNICODETEXT).

    Nao usamos o `clip.exe`: ele le a entrada na codepage do console (OEM) e
    entrega "justificativa" com acento quebrado — logo, um prompt quebrado.
    A API do Windows fala UTF-16 nativamente; e o unico jeito de o texto do aluno
    chegar inteiro na IA.
    """
    import ctypes
    from ctypes import wintypes

    CF_UNICODETEXT, GMEM_MOVEABLE = 13, 0x0002
    u32, k32 = ctypes.WinDLL("user32", use_last_error=True), ctypes.WinDLL("kernel32", use_last_error=True)
    # Sem argtypes, o ctypes trunca o handle (um ponteiro de 64 bits) num int de 32:
    # "int too long to convert". Declarar as assinaturas nao e' preciosismo aqui.
    k32.GlobalAlloc.argtypes, k32.GlobalAlloc.restype = (wintypes.UINT, ctypes.c_size_t), wintypes.HGLOBAL
    k32.GlobalLock.argtypes, k32.GlobalLock.restype = (wintypes.HGLOBAL,), wintypes.LPVOID
    k32.GlobalUnlock.argtypes, k32.GlobalFree.argtypes = (wintypes.HGLOBAL,), (wintypes.HGLOBAL,)
    u32.OpenClipboard.argtypes, u32.OpenClipboard.restype = (wintypes.HWND,), wintypes.BOOL
    u32.SetClipboardData.argtypes = (wintypes.UINT, wintypes.HANDLE)
    u32.SetClipboardData.restype = wintypes.HANDLE

    dados = texto.replace("\r\n", "\n").replace("\n", "\r\n") + "\0"
    buf = dados.encode("utf-16-le")
    if not u32.OpenClipboard(None):
        return False
    try:
        u32.EmptyClipboard()
        h = k32.GlobalAlloc(GMEM_MOVEABLE, len(buf))
        if not h:
            return False
        p = k32.GlobalLock(h)
        if not p:
            k32.GlobalFree(h)
            return False
        ctypes.memmove(p, buf, len(buf))
        k32.GlobalUnlock(h)
        if not u32.SetClipboardData(CF_UNICODETEXT, h):
            k32.GlobalFree(h)      # so' e' nosso enquanto o clipboard nao o aceita
            return False
        return True
    finally:
        u32.CloseClipboard()


def copiar(texto):
    """Poe `texto` na area de transferencia. True se conseguiu.

    Nunca levanta excecao: sem clipboard (servidor, terminal sem X11) o script
    continua e mostra o cartao do quadro / o arquivo — o passo nao pode morrer por
    causa de uma comodidade.
    """
    if not texto:
        return False
    try:
        if sys.platform == "win32":
            return _copiar_windows(texto)
        for cmd in _COPIADORES_UNIX.get(sys.platform, _COPIADORES_UNIX[None]):
            try:
                p = subprocess.run(cmd, input=texto.encode("utf-8"),
                                   stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
                if p.returncode == 0:
                    return True
            except (OSError, subprocess.SubprocessError):
                continue
    except Exception:
        pass
    return False


# ─────────────────────────────────────────────────────────────────────────────
# LER TEXTO LONGO (varias linhas) — o contrario do copiar()
#
# POR QUE ISTO EXISTE (Eduardo, 2026-07-30): o `input()` le UMA linha. Quando o
# aluno COLA um texto de varias linhas num prompt de `input()`, o console entrega
# cada quebra de linha como se fosse um Enter: a 1a linha vira a resposta e TODAS
# as outras ficam na fila, sendo consumidas pelas perguntas SEGUINTES do script.
# No Passo 9 isso e' pior do que perder texto — as linhas restantes respondiam
# sozinhas os interruptores s/n do detalhamento (escopo? riscos? cronograma?), e o
# aluno nem via acontecer. Um pedido de mudanca serio tem varias linhas (um rascunho
# de EAP, uma lista de filiais); ele PRECISA caber na entrada.
#
# A saida tem duas portas, e as duas evitam o problema pela raiz:
#   V + Enter  -> lemos da AREA DE TRANSFERENCIA (o aluno ja copiou o texto de algum
#                 lugar; nada passa pelo console, entao nada se perde)
#   colar + .  -> o aluno cola no console mesmo e fecha com um ponto sozinho numa
#                 linha. O terminador e' explicito de proposito: linha em branco NAO
#                 serve, porque um rascunho de EAP tem linhas em branco no meio.
# ─────────────────────────────────────────────────────────────────────────────
_COLADORES_UNIX = {"darwin": [["pbpaste"]],
                   None: [["xclip", "-selection", "clipboard", "-o"],
                          ["xsel", "--clipboard", "--output"]]}


def _colar_windows():
    """Le CF_UNICODETEXT da area de transferencia do Windows. '' se nao houver texto."""
    import ctypes
    from ctypes import wintypes

    CF_UNICODETEXT = 13
    u32 = ctypes.WinDLL("user32", use_last_error=True)
    k32 = ctypes.WinDLL("kernel32", use_last_error=True)
    # As mesmas assinaturas do _copiar_windows: sem elas o ctypes trunca o handle.
    u32.OpenClipboard.argtypes, u32.OpenClipboard.restype = (wintypes.HWND,), wintypes.BOOL
    u32.GetClipboardData.argtypes = (wintypes.UINT,)
    u32.GetClipboardData.restype = wintypes.HANDLE
    k32.GlobalLock.argtypes, k32.GlobalLock.restype = (wintypes.HGLOBAL,), wintypes.LPVOID
    k32.GlobalUnlock.argtypes = (wintypes.HGLOBAL,)

    if not u32.OpenClipboard(None):
        return ""
    try:
        h = u32.GetClipboardData(CF_UNICODETEXT)
        if not h:
            return ""
        p = k32.GlobalLock(h)
        if not p:
            return ""
        try:
            return ctypes.wstring_at(p)
        finally:
            k32.GlobalUnlock(h)
    finally:
        u32.CloseClipboard()


def colar():
    """O texto que esta na area de transferencia (''  se nao der para ler).

    Espelho do copiar(): nunca levanta: sem clipboard o chamador cai na digitacao.
    """
    def _lf(s):
        # O clipboard do Windows guarda CRLF. Devolvemos sempre \n: o texto lido aqui
        # vai virar prompt e cartao, e um \r perdido no meio de uma linha estraga tanto
        # o parser (que compara nomes) quanto o visual do cartao.
        return (s or "").replace("\r\n", "\n").replace("\r", "\n")

    try:
        if sys.platform == "win32":
            return _lf(_colar_windows())
        for cmd in _COLADORES_UNIX.get(sys.platform, _COLADORES_UNIX[None]):
            try:
                p = subprocess.run(cmd, capture_output=True)
                if p.returncode == 0:
                    return _lf(p.stdout.decode("utf-8", errors="replace"))
            except (OSError, subprocess.SubprocessError):
                continue
    except Exception:
        pass
    return ""


def _preview(texto, linhas=6):
    """As primeiras linhas de um texto, para o aluno CONFERIR antes de eu usar."""
    ls = [l for l in (texto or "").splitlines()]
    corte = ls[:linhas]
    saida = "\n".join("     | " + l[:88] for l in corte)
    if len(ls) > linhas:
        saida += f"\n     | ... (+{len(ls) - linhas} linha(s))"
    return saida


def entrada_longa(pergunta, dica="", vazio_ok=True):
    """Le um texto de VARIAS linhas do aluno. Devolve a string (pode ser '').

    Tres caminhos, na ordem em que o aluno costuma querer:
      V + Enter   le da area de transferencia (o mais seguro para texto colado)
      colar + .   cola no console e fecha com um '.' sozinho numa linha
      Enter       nada (quando `vazio_ok`)
    """
    print(f"\n{pergunta}")
    if dica:
        print(dica)
    print(T("   Como responder (o texto pode ter QUANTAS linhas você quiser):"))
    print(T("     • já copiou o texto?  tecle  V  e Enter — eu leio da área de transferência"))
    print(T("     • prefere colar aqui? cole (Ctrl+V) e, numa linha NOVA, digite  .  e Enter"))
    if vazio_ok:
        print(T("     • só Enter (linha vazia) = seguir sem texto"))
    print()

    linhas = []
    while True:
        try:
            l = input("   | " if linhas else "   > ")
        except EOFError:                     # Ctrl+Z (Windows) / Ctrl+D: fecha o texto
            break
        except KeyboardInterrupt:
            return ""
        if not linhas and l.strip().lower() == "v":
            texto = (colar() or "").strip()
            if not texto:
                print(T("   (não achei texto na área de transferência — pode colar aqui mesmo,"
                      " terminando com um '.' sozinho numa linha)\n"))
                continue
            print(T("\n   Li isto da área de transferência:"))
            print(_preview(texto))
            r = input(T("\n   Uso este texto? [S/n] ")).strip().lower()
            if r in ("", "s", "sim", "y", "yes"):
                return texto
            print(T("   Ok — cole aqui então (termine com um '.' sozinho numa linha).\n"))
            continue
        if not linhas and not l.strip() and vazio_ok:
            return ""
        if l.strip() in (".", "fim", "FIM", "Fim"):
            break
        linhas.append(l)
    return "\n".join(linhas).strip()
