#!/usr/bin/env python3
"""Anúncio nativo de LIVE do Instagram ("Assistir ao vivo") para QUALQUER conta.

Estrutura = campanha [LIVE NATIVO] original de 30/09/2026 21h:
Engajamento · 1 conjunto quente · ThruPlay · destino INSTAGRAM_LIVE · só Stories+Reels ·
verba total colada na janela da live · Advantage de público desligado · sem exclusão.

Cada cliente tem um PERFIL (arquivo JSON em ~/.claude/live-nativo/<perfil>.json) com
token, conta, Página, Instagram, públicos e card.

  python3 live_nativo.py configurar --perfil maria --token EAA... --conta act_123 [--nome "Maria"]
  python3 live_nativo.py checar   --perfil maria
  python3 live_nativo.py plano    --perfil maria --fim 22:00 [--verba 1000] [--data ... --inicio ...]
  python3 live_nativo.py criar    --perfil maria --fim 22:00 [--verba 1000] [--data ... --inicio ...]
  python3 live_nativo.py auditar  --perfil maria CAMPANHA_ID
  python3 live_nativo.py ligar    --perfil maria CAMPANHA_ID
  python3 live_nativo.py status   --perfil maria CAMPANHA_ID
  python3 live_nativo.py apagar   --perfil maria CAMPANHA_ID
"""
import argparse
import datetime as dt
import json
import os
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
import uuid

try:
    sys.stdout.reconfigure(line_buffering=True)
except AttributeError:
    pass

API = "https://graph.facebook.com/v22.0"
try:
    from zoneinfo import ZoneInfo
except ImportError:  # Python antigo: fica no horário de Brasília
    ZoneInfo = None
TZ = ZoneInfo("America/Sao_Paulo") if ZoneInfo else dt.timezone(dt.timedelta(hours=-3))
MOEDA = "BRL"
SIMBOLO = {"BRL": "R$", "USD": "US$", "EUR": "€", "GBP": "£", "MXN": "MX$", "ARS": "AR$",
           "CLP": "CLP$", "COP": "COP$", "PEN": "S/", "CAD": "CA$", "AUD": "AU$"}
DIR_PERFIS = os.path.expanduser("~/.claude/live-nativo")
SKILL = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
VERBA_MINIMA = 60  # a Meta recusou R$ 50 em 01/10 ("pelo menos R$ 57" pra live do Instagram)
PERMISSOES = ["ads_management", "pages_show_list", "pages_read_engagement",
              "pages_manage_ads", "instagram_basic"]



# ---------------------------------------------------------------- API ----------
class ApiError(Exception):
    def __init__(self, payload):
        err = payload.get("error", {})
        self.code, self.sub = err.get("code"), err.get("error_subcode")
        self.msg = err.get("error_user_msg") or err.get("message") or json.dumps(payload)
        super().__init__(f"[{self.code}/{self.sub}] {self.msg}")


class Meta:
    def __init__(self, token):
        self.token = token

    def _req(self, method, path, params=None, files=None):
        params = dict(params or {}, access_token=self.token)
        enc = {k: json.dumps(v, ensure_ascii=False) if isinstance(v, (dict, list)) else str(v)
               for k, v in params.items()}
        url = f"{API}/{path.lstrip('/')}"
        delay = 8
        for tentativa in range(6):
            try:
                if method == "GET":
                    req = urllib.request.Request(f"{url}?{urllib.parse.urlencode(enc)}")
                elif files:
                    bnd = uuid.uuid4().hex
                    body = b""
                    for k, v in enc.items():
                        body += f"--{bnd}\r\nContent-Disposition: form-data; name=\"{k}\"\r\n\r\n{v}\r\n".encode()
                    for k, (nome, dados) in files.items():
                        body += (f"--{bnd}\r\nContent-Disposition: form-data; name=\"{k}\"; filename=\"{nome}\"\r\n"
                                 "Content-Type: image/jpeg\r\n\r\n").encode() + dados + b"\r\n"
                    body += f"--{bnd}--\r\n".encode()
                    req = urllib.request.Request(url, data=body, method="POST",
                                                 headers={"Content-Type": f"multipart/form-data; boundary={bnd}"})
                else:
                    req = urllib.request.Request(url, data=urllib.parse.urlencode(enc).encode(), method=method)
                with urllib.request.urlopen(req, timeout=120) as r:
                    return json.loads(r.read().decode())
            except urllib.error.HTTPError as e:
                try:
                    payload = json.loads(e.read().decode())
                except Exception:
                    payload = {"error": {"message": f"HTTP {e.code}"}}
                if payload.get("error", {}).get("code") in (4, 17, 32, 613) and tentativa < 5:
                    print(f"  ⏳ Meta pediu pausa (limite de chamadas), esperando {delay}s")
                    time.sleep(delay)
                    delay *= 2
                    continue
                raise ApiError(payload)
            except OSError as e:
                if tentativa < 5:
                    time.sleep(delay)
                    continue
                raise ApiError({"error": {"message": f"sem conexão: {e}"}})

    def get(self, path, **p):
        return self._req("GET", path, p)

    def post(self, path, files=None, **p):
        return self._req("POST", path, p, files)

    def todos(self, path, **p):
        p.setdefault("limit", 100)
        itens, r = [], self.get(path, **p)
        while True:
            itens += r.get("data", [])
            nxt = r.get("paging", {}).get("cursors", {}).get("after")
            if not nxt or not r.get("paging", {}).get("next"):
                return itens
            r = self.get(path, **dict(p, after=nxt))


# ------------------------------------------------------------- perfis ----------
def carregar_perfil(nome):
    arq = os.path.join(DIR_PERFIS, f"{nome}.json")
    if os.path.exists(arq):
        perfil = json.load(open(arq))
    else:
        sys.exit(f"✗ Perfil '{nome}' não existe. Rode primeiro:\n"
                 f"  python3 live_nativo.py configurar --perfil {nome} --token SEU_TOKEN --conta act_XXXX")
    if not perfil.get("token"):
        arq_tk = os.path.expanduser(perfil.get("token_arquivo", ""))
        if not os.path.exists(arq_tk):
            sys.exit(f"✗ Token não encontrado ({arq_tk}). Rode 'configurar' com --token.")
        for linha in open(arq_tk):
            if linha.strip().startswith(perfil["token_chave"] + "="):
                perfil["token"] = linha.split("=", 1)[1].strip().strip("'\"")
    return perfil


def salvar_perfil(nome, perfil):
    os.makedirs(DIR_PERFIS, exist_ok=True)
    arq = os.path.join(DIR_PERFIS, f"{nome}.json")
    with open(arq, "w") as fh:
        json.dump(perfil, fh, ensure_ascii=False, indent=2)
    os.chmod(arq, 0o600)
    return arq


def brl(v):
    """Valor na moeda da conta (R$ 1.234,56 / US$ 1.234,56)."""
    num = f"{v:,.2f}".replace(",", "X").replace(".", ",").replace("X", ".")
    return f"{SIMBOLO.get(MOEDA, MOEDA)} {num}"


def usar_conta(p):
    """Fuso e moeda da conta de anúncios: as horas da live valem no fuso da conta."""
    global TZ, MOEDA
    MOEDA = p.get("moeda", "BRL")
    if ZoneInfo and p.get("fuso"):
        try:
            TZ = ZoneInfo(p["fuso"])
        except Exception:
            pass


def link_live(p):
    return f"http://instagram.com/{p['ig_username']}/live"


# --------------------------------------------------------- configurar ----------
def gerar_card(nome):
    """Card 1080x1920 'FULANA ESTÁ AO VIVO AGORA'. Sem Pillow, usa o card genérico."""
    generico = os.path.join(SKILL, "assets", "card-ao-vivo-generico.jpg")
    try:
        from PIL import Image, ImageDraw, ImageFont
    except ImportError:
        print("  (Pillow não instalado, usando o card genérico 'AO VIVO AGORA')")
        return open(generico, "rb").read()

    def fonte(tam):
        for f in ("/System/Library/Fonts/Supplemental/Arial Bold.ttf",
                  "/System/Library/Fonts/Helvetica.ttc",
                  "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf",
                  "C:\\Windows\\Fonts\\arialbd.ttf"):
            if os.path.exists(f):
                return ImageFont.truetype(f, tam)
        return ImageFont.load_default()

    W, H = 1080, 1920
    img = Image.new("RGB", (W, H))
    d = ImageDraw.Draw(img)
    for y in range(H):  # degradê verde escuro → verde
        t = y / H
        d.line([(0, y), (W, y)], fill=(int(10 + 21 * t), int(38 + 69 * t), int(30 + 49 * t)))

    def centro(y, txt, f, cor):
        w = d.textbbox((0, 0), txt, font=f)[2]
        d.text(((W - w) / 2, y), txt, font=f, fill=cor)

    d.rounded_rectangle([350, 600, 730, 690], radius=45, fill=(229, 30, 50))
    d.ellipse([385, 630, 415, 660], fill="white")
    d.text((435, 615), "AO VIVO", font=fonte(52), fill="white")
    topo = f"{nome.upper()} ESTÁ" if nome else "ESTOU"
    f_topo = fonte(80)
    while d.textbbox((0, 0), topo, font=f_topo)[2] > W - 120 and f_topo.size > 40:
        f_topo = fonte(f_topo.size - 4)
    centro(760, topo, f_topo, "white")
    centro(880, "AO VIVO", fonte(150), (255, 214, 0))
    centro(1040, "AGORA", fonte(150), (255, 214, 0))
    centro(1250, "Toca em \"Assistir ao vivo\"", fonte(54), "white")
    d.polygon([(500, 1350), (580, 1350), (540, 1410)], fill=(255, 214, 0))
    caminho = os.path.join(DIR_PERFIS, f"card-{uuid.uuid4().hex[:6]}.jpg")
    os.makedirs(DIR_PERFIS, exist_ok=True)
    img.save(caminho, "JPEG", quality=92)
    print(f"  card gerado: {caminho}")
    return open(caminho, "rb").read()


def classificar_publico(a):
    """'instagram' (IG_BUSINESS: quem engajou com o perfil) / 'video' (ENGAGEMENT com
    nome de vídeo) / None. Só públicos com tamanho pra entregar e entrega ok."""
    if (a.get("approximate_count_lower_bound") or 0) < 1000:
        return None
    if (a.get("delivery_status") or {}).get("code") not in (200, None):
        return None
    nome = a.get("name", "").lower()
    if a.get("subtype") == "IG_BUSINESS":
        return "instagram"
    if a.get("subtype") == "ENGAGEMENT" and any(k in nome for k in ("vv", "video", "vídeo", "view", "thruplay")):
        return "video"
    return None


def do_perfil(m, aud_id, ig_id):
    """Confere na regra se o público IG é do Instagram da cliente (conta pode ter outros perfis)."""
    try:
        return ig_id in json.dumps(m.get(aud_id, fields="rule").get("rule") or "")
    except ApiError:
        return True  # regra grande demais pra ler: mantém e mostra na lista pra conferência


def verificar_token(token):
    """Passo 1 do guia: o token funciona? Vence quando? Tem as permissões? Que contas enxerga?"""
    m = Meta(token)
    try:
        eu = m.get("me", fields="id,name")
    except ApiError as e:
        sys.exit(f"✗ TOKEN NÃO FUNCIONA: {e}\n  → Gere um token novo (SKILL.md, 'Como gerar o token') e cole de novo.")
    print(f"✓ Token válido · pertence a: {eu.get('name')}")
    try:
        info = m.get("debug_token", input_token=token)["data"]
        vence = info.get("expires_at") or 0
        if vence == 0:
            print("✓ Validade: não vence (token permanente)")
        else:
            quando = dt.datetime.fromtimestamp(vence, TZ)
            horas = (quando - dt.datetime.now(TZ)).total_seconds() / 3600
            print(f"{'⚠️' if horas < 72 else '✓'} Validade: vence em {quando:%d/%m %H:%M}"
                  + ("  → token TEMPORÁRIO. Funciona hoje, mas gere um permanente (usuário do sistema)."
                     if horas < 72 else ""))
    except ApiError:
        pass
    perms = {x["permission"] for x in m.get("me/permissions").get("data", []) if x["status"] == "granted"}
    faltam = [x for x in PERMISSOES if x not in perms]
    if faltam and perms:
        print(f"⚠️ Permissões que faltam no token: {', '.join(faltam)}")
        if "ads_management" in faltam:
            sys.exit("✗ Sem 'ads_management' não dá pra criar anúncio. Gere o token marcando as permissões da SKILL.md.")
    else:
        print("✓ Permissões ok")
    contas = []
    try:
        contas = m.todos("me/adaccounts", fields="name,account_id,account_status,currency")
    except ApiError:
        pass
    if contas:
        print("\nContas de anúncios que esse token enxerga:")
        for c in contas:
            st = "ativa" if c.get("account_status") == 1 else f"⚠️ status {c.get('account_status')} (não ativa)"
            print(f"  act_{c['account_id']}  {c['name']}  ({c.get('currency')}, {st})")
    else:
        print("⚠️ O token não enxerga nenhuma conta de anúncios. Dar acesso à conta pro usuário do token.")
    return m, contas


def buscar_publicos_quentes(m, conta, ig_id):
    """Quem já conhece a pessoa: engajou com o Instagram (IG_BUSINESS) ou viu vídeos (ENGAGEMENT)."""
    pubs = m.todos(f"{conta}/customaudiences",
                   fields="id,name,subtype,approximate_count_lower_bound,delivery_status")
    cand = [(classificar_publico(x), x) for x in pubs]
    cand = [(t, x) for t, x in cand if t]
    cand.sort(key=lambda tx: -(tx[1].get("approximate_count_lower_bound") or 0))
    ig_pubs = []
    for x in (x for t, x in cand if t == "instagram"):
        if len(ig_pubs) == 8:
            break
        if do_perfil(m, x["id"], ig_id):
            ig_pubs.append(x)
    vid_pubs = [x for t, x in cand if t == "video"][:15 - len(ig_pubs)]
    return ig_pubs + vid_pubs, pubs


def avaliar_base(quentes):
    """Diz em português se a base quente é boa pra live. Tamanhos se sobrepõem: usa o maior."""
    maior = max([x.get("approximate_count_lower_bound") or 0 for x in quentes] or [0])
    n = f"{maior:,}".replace(",", ".")
    if not quentes:
        return "NENHUMA", ("Não existe público de quem já te conhece. A live vai depender de gente que nunca "
                           "te viu, e o resultado cai muito. Crie os públicos quentes (passo 'publicos --criar').")
    if maior < 5000:
        return "PEQUENA", (f"Base quente pequena (maior público ~{n} pessoas). Funciona, mas a verba gasta "
                           "rápido e fica cara. Use verba menor (R$ 60–150) e crie os públicos que faltam.")
    if maior < 20000:
        return "MÉDIA", f"Base quente boa (maior público ~{n} pessoas). Verba sugerida R$ 150–400."
    return "FORTE", f"Base quente forte (maior público ~{n} pessoas). Aguenta R$ 400–800."


PUBLICOS_PADRAO = [
    ("IG Engajados 365D", "ig_business_profile_all", 365),
    ("IG Engajados 90D", "ig_business_profile_all", 90),
    ("IG Visitantes do perfil 365D", "ig_business_profile_visit", 365),
    ("IG Mandaram mensagem 365D", "ig_user_messaged_business", 365),
]


def criar_publicos(m, conta, ig_id, ig_user):
    """Cria os públicos quentes básicos do Instagram (não gasta nada). Pula os que já existem."""
    existentes = {x["name"] for x in m.todos(f"{conta}/customaudiences", fields="name")}
    criados = []
    for nome, evento, dias in PUBLICOS_PADRAO:
        nome_full = f"[LIVE] {nome} · @{ig_user}"
        if nome_full in existentes:
            print(f"  = já existe: {nome_full}")
            continue
        try:
            r = m.post(f"{conta}/customaudiences", name=nome_full, prefill=1,
                       rule={"inclusions": {"operator": "or", "rules": [{
                           "event_sources": [{"id": ig_id, "type": "ig_business"}],
                           "retention_seconds": dias * 86400,
                           "filter": {"operator": "and", "filters": [
                               {"field": "event", "operator": "eq", "value": evento}]}}]}})
            criados.append(r["id"])
            print(f"  + criado: {nome_full}")
        except ApiError as e:
            print(f"  ✗ {nome_full}: {e}")
    if criados:
        print("  ⏳ Públicos novos levam de alguns minutos a algumas horas pra encher. "
              "Rode 'publicos' de novo depois pra ver o tamanho.")
    return criados


def achar_pagina(m, conta, pagina=None):
    paginas = []
    for fonte_pg in (f"{conta}/promote_pages", "me/accounts"):
        try:
            paginas += m.todos(fonte_pg, fields="id,name,instagram_business_account{id,username,ig_id}")
        except ApiError:
            pass
    vistos, lista = set(), []
    for pg in paginas:
        if pg["id"] not in vistos:
            vistos.add(pg["id"])
            lista.append(pg)
    if pagina:
        lista = [pg for pg in lista if pg["id"] == pagina] or [m.get(
            pagina, fields="id,name,instagram_business_account{id,username,ig_id}")]
    for pg in lista:
        ig = pg.get("instagram_business_account")
        print(f"   {pg['id']}  {pg['name']}  →  {'@' + ig['username'] if ig else 'SEM Instagram ligado'}")
    com_ig = [pg for pg in lista if pg.get("instagram_business_account")]
    if not com_ig:
        sys.exit("✗ Nenhuma Página com Instagram profissional ligado.\n"
                 "  → No Instagram: Editar perfil → Página → conectar a Página do Facebook "
                 "(ou Central de Contas). O Instagram precisa ser conta profissional. Depois rode de novo.")
    if len(com_ig) > 1 and not pagina:
        sys.exit("✗ Mais de uma Página com Instagram. Pergunte qual é a da live e repita com --pagina ID.")
    return com_ig[0]


def configurar(a):
    print("1) TOKEN")
    m, contas = verificar_token(a.token)
    if not a.conta:
        ativas = [c for c in contas if c.get("account_status") == 1]
        if len(ativas) == 1:
            a.conta = ativas[0]["account_id"]
            print(f"   → só uma conta ativa, usando act_{a.conta}")
        else:
            sys.exit("\nPergunte qual conta de anúncios usar e repita com --conta act_XXXX.")
    conta = a.conta if a.conta.startswith("act_") else f"act_{a.conta}"

    print(f"\n2) CONTA DE ANÚNCIOS {conta}")
    try:
        c = m.get(conta, fields="name,currency,account_status,timezone_name")
    except ApiError as e:
        sys.exit(f"✗ O token não enxerga essa conta: {e}")
    print(f"   {c['name']} · moeda {c['currency']} · fuso {c.get('timezone_name')}")
    if c["account_status"] != 1:
        print("   ⚠️ CONTA NÃO ESTÁ ATIVA (pagamento pendente ou bloqueio). Resolver no Gerenciador antes da live.")

    print("\n3) PÁGINA E INSTAGRAM")
    pg = achar_pagina(m, conta, a.pagina)
    ig = pg["instagram_business_account"]
    print(f"   ✓ usando {pg['name']} → @{ig['username']}")

    print("\n4) PÚBLICO QUENTE (quem já te conhece)")
    if a.publicos:
        ids = set(a.publicos.split(","))
        quentes = [x for x in m.todos(f"{conta}/customaudiences",
                                      fields="id,name,approximate_count_lower_bound") if x["id"] in ids]
    else:
        quentes, _ = buscar_publicos_quentes(m, conta, ig["id"])
    for x in quentes:
        print(f"   ✓ {x['name']:<55} {x.get('approximate_count_lower_bound')}")
    nivel, conselho = avaliar_base(quentes)
    print(f"   BASE QUENTE: {nivel}. {conselho}")

    print("\n5) CARD 'AO VIVO'")
    dados = open(a.card, "rb").read() if a.card else gerar_card(a.nome)
    up = m.post(f"{conta}/adimages", files={"filename": ("card-ao-vivo.jpg", dados)})
    image_hash = list(up["images"].values())[0]["hash"]
    print(f"   ✓ card enviado pra conta")

    perfil = {
        "nome": a.nome or pg["name"], "token": a.token, "conta": conta, "pagina": pg["id"],
        "pagina_nome": pg["name"], "ig_user_id": ig["id"], "ig_username": ig["username"],
        "ig_id": str(ig.get("ig_id", "")), "image_hash": image_hash,
        "texto": a.texto or "Estou ao vivo agora! Entra na live e vem participar comigo.",
        "pais": a.pais, "idade_min": 18, "idade_max": 65,
        "fuso": c.get("timezone_name") or "America/Sao_Paulo", "moeda": c.get("currency") or "BRL",
        "publicos": [[x["id"], x["name"]] for x in quentes],
    }
    arq = salvar_perfil(a.perfil, perfil)
    print(f"\n✓ CONFIGURAÇÃO SALVA (perfil '{a.perfil}', {arq})")
    if nivel in ("NENHUMA", "PEQUENA"):
        print(f"  Próximo: criar públicos quentes → python3 live_nativo.py publicos --perfil {a.perfil} --criar")
    else:
        print(f"  Próximo: python3 live_nativo.py plano --perfil {a.perfil} --fim HH:MM --verba VALOR")


def cmd_publicos(m, p, a):
    """Mostra a base quente atual; com --criar, cria os básicos e já atualiza o perfil."""
    if a.criar:
        print("Criando públicos quentes do Instagram (não gasta nada):")
        criar_publicos(m, p["conta"], p["ig_user_id"], p["ig_username"])
        time.sleep(5)
    quentes, _ = buscar_publicos_quentes(m, p["conta"], p["ig_user_id"])
    # público recém-criado ainda sem tamanho: entra mesmo assim (enche até a live)
    for x in m.todos(f"{p['conta']}/customaudiences", fields="id,name,approximate_count_lower_bound"):
        if x["name"].startswith("[LIVE] ") and x["name"].endswith(f"@{p['ig_username']}") \
                and x["id"] not in {q["id"] for q in quentes}:
            quentes.append(x)
    print("\nPúblicos quentes encontrados:")
    for x in quentes:
        n = x.get("approximate_count_lower_bound")
        print(f"   ✓ {x['name']:<55} {n if n and n > 0 else 'enchendo…'}")
    nivel, conselho = avaliar_base(quentes)
    print(f"\nBASE QUENTE: {nivel}. {conselho}")
    if quentes:
        p["publicos"] = [[x["id"], x["name"]] for x in quentes]
        salvar_perfil(a.perfil, p)
        print(f"✓ Perfil '{a.perfil}' atualizado com {len(quentes)} públicos.")


# ------------------------------------------------------------- estrutura -------
def targeting(p):
    return {
        "age_min": p["idade_min"], "age_max": p["idade_max"],
        "custom_audiences": [{"id": i} for i, _ in p["publicos"]],
        "geo_locations": {"countries": [p["pais"]], "location_types": ["home", "recent"]},
        "targeting_relaxation_types": {"lookalike": 0, "custom_audience": 1},
        "targeting_automation": {"advantage_audience": 0},
        "publisher_platforms": ["instagram"],
        "instagram_positions": ["story", "reels"],
    }


def creative_spec(p):
    link = link_live(p)
    valor = {"app_destination": "INSTAGRAM_LIVE_VIDEO", "link": link}
    if p.get("ig_id"):
        valor["app_link"] = f"instagram://user?username={p['ig_username']}&userid={p['ig_id']}"
    return {"page_id": p["pagina"], "instagram_user_id": p["ig_user_id"],
            "link_data": {"link": link, "message": p["texto"], "image_hash": p["image_hash"],
                          "call_to_action": {"type": "WATCH_LIVE_VIDEO", "value": valor}}}


def live_no_ar(m, p):
    try:
        return m.get(f"{p['ig_user_id']}/live_media", fields="id,timestamp").get("data", [])
    except ApiError:
        return []


def checar(m, p):
    ok = True
    print(f"Perfil: {p['nome']} · conta {p['conta']} · Página {p['pagina']} · @{p['ig_username']}")
    try:
        c = m.get(p["conta"], fields="name,account_status")
        print(f"  ✓ conta {c['name']} (status {c['account_status']})")
        ok &= c["account_status"] == 1
    except ApiError as e:
        print(f"  ✗ conta: {e}")
        return False
    print(f"  Públicos ({len(p['publicos'])}):")
    for i, nome in p["publicos"]:
        try:
            r = m.get(i, fields="approximate_count_lower_bound,delivery_status")
            st = (r.get("delivery_status") or {}).get("code")
            print(f"    {'✓' if st in (200, None) else '⚠️'} {nome:<52} {r.get('approximate_count_lower_bound')}")
        except ApiError as e:
            ok = False
            print(f"    ✗ {nome}: {e}")
    lives = live_no_ar(m, p)
    print(f"  Live no ar agora: {'SIM' if lives else 'não'}")
    return ok


def criar(m, p, a):
    agora = dt.datetime.now(TZ)
    data = dt.date.fromisoformat(a.data) if a.data else agora.date()

    def hora(hhmm):
        h, mi = map(int, hhmm.split(":"))
        return dt.datetime.combine(data, dt.time(h, mi), TZ)

    inicio = hora(a.inicio) if a.inicio else agora + dt.timedelta(minutes=2)
    if inicio < agora + dt.timedelta(minutes=1):
        inicio = agora + dt.timedelta(minutes=2)
    fim = hora(a.fim)
    if fim <= inicio:
        fim += dt.timedelta(days=1)  # live que passa da meia-noite
    if not p.get("publicos"):
        sys.exit(f"✗ Sem público quente no perfil. Rode: python3 live_nativo.py publicos --perfil {a.perfil} --criar")
    if MOEDA == "BRL" and a.verba < VERBA_MINIMA:
        sys.exit(f"✗ Verba mínima pra anúncio de live do Instagram é {brl(VERBA_MINIMA)} (a Meta recusa abaixo de R$ 57).")
    rot = f"{inicio:%d/%m %H}h"
    minutos = int((fim - inicio).total_seconds() // 60)

    print(f"PLANO — {p['nome']} · estrutura da [LIVE NATIVO] 30/09")
    print(f"  Campanha : [LIVE NATIVO] {rot} · Engajamento")
    print(f"  Conjunto : QUENTE · ThruPlay · destino LIVE do Instagram · Página {p['pagina']}")
    print(f"  Janela   : {inicio:%d/%m %H:%M} → {fim:%d/%m %H:%M} ({minutos} min)")
    print(f"  Verba    : {brl(a.verba)} TOTAL ({brl(a.verba / max(minutos, 1))}/min)")
    print(f"  Onde     : só Instagram · Stories + Reels · {p['pais']} · {p['idade_min']}-{p['idade_max']} · sem expansão automática")
    print(f"  Anúncio  : card AO VIVO + \"{p['texto']}\" · botão Assistir ao vivo → @{p['ig_username']}")
    print(f"  Públicos : {len(p['publicos'])} · exclusões: nenhuma")
    if not a.executar:
        print("\n(só o plano — nada foi criado)")
        return
    print("\nCriando PAUSADO…")
    camp = m.post(f"{p['conta']}/campaigns",
                  name=f"[LIVE NATIVO] {rot} · Anunciar vídeo ao vivo · Quente · Stories/Reels",
                  objective="OUTCOME_ENGAGEMENT", buying_type="AUCTION", special_ad_categories=[],
                  is_adset_budget_sharing_enabled=False, stop_time=fim.isoformat(), status="PAUSED")["id"]
    print(f"  campanha {camp}")
    try:
        cj = m.post(f"{p['conta']}/adsets",
                    name=f"[LIVE NATIVO] {inicio:%d/%m} · QUENTE · {len(p['publicos'])} públicos",
                    campaign_id=camp, optimization_goal="THRUPLAY", billing_event="IMPRESSIONS",
                    bid_strategy="LOWEST_COST_WITHOUT_CAP", destination_type="INSTAGRAM_LIVE",
                    promoted_object={"page_id": p["pagina"]}, lifetime_budget=int(round(a.verba * 100)),
                    start_time=inicio.isoformat(), end_time=fim.isoformat(), targeting=targeting(p),
                    status="PAUSED")["id"]
        print(f"  conjunto {cj}")
        cr = m.post(f"{p['conta']}/adcreatives", name=f"[LIVE NATIVO] {rot} card",
                    object_story_spec=creative_spec(p))["id"]
        print(f"  criativo {cr}")
        ad = m.post(f"{p['conta']}/ads", name=f"[LIVE NATIVO] {inicio:%d/%m} · anúncio", adset_id=cj,
                    creative={"creative_id": cr}, status="PAUSED")["id"]
        print(f"  anúncio {ad}")
    except ApiError:
        m.post(camp, status="DELETED")  # não deixa campanha vazia pra trás
        print("  (campanha incompleta apagada)")
        raise
    auditar(m, p, camp)
    print(f"\n✓ CAMPANHA {camp} criada PAUSADA. Ligar só com OK:\n"
          f"  python3 live_nativo.py ligar --perfil {a.perfil} {camp}")


def auditar(m, p, camp):
    c = m.get(camp, fields="name,objective,status")
    sets = m.get(f"{camp}/adsets", fields="optimization_goal,destination_type,promoted_object,"
                 "lifetime_budget,start_time,end_time,targeting")["data"]
    ads = m.get(f"{camp}/ads", fields="status,effective_status,creative{object_story_spec,call_to_action_type}")["data"]
    L = [("Campanha", c["name"], True), ("Objetivo", c["objective"], c["objective"] == "OUTCOME_ENGAGEMENT")]
    for s in sets:
        t = s["targeting"]
        ids = {x["id"] for x in t.get("custom_audiences", [])}
        L += [("Meta do conjunto", s["optimization_goal"], s["optimization_goal"] == "THRUPLAY"),
              ("Destino", s.get("destination_type"), s.get("destination_type") == "INSTAGRAM_LIVE"),
              ("Página", s["promoted_object"].get("page_id"), s["promoted_object"].get("page_id") == p["pagina"]),
              ("Verba total", brl(int(s["lifetime_budget"]) / 100),
               int(s["lifetime_budget"]) >= (VERBA_MINIMA * 100 if MOEDA == "BRL" else 1)),
              ("Janela", f"{s['start_time'][:16].replace('T', ' ')} → {s['end_time'][11:16]}", True),
              ("Públicos", f"{len(ids)}/{len(p['publicos'])}", ids == {i for i, _ in p["publicos"]}),
              ("Idade", f"{t.get('age_min')}-{t.get('age_max')}", True),
              ("Posições", "+".join(t.get("instagram_positions", [])),
               sorted(t.get("instagram_positions", [])) == ["reels", "story"]),
              ("Expansão automática", "desligada" if t.get("targeting_automation", {}).get("advantage_audience") == 0
               else "LIGADA", t.get("targeting_automation", {}).get("advantage_audience") == 0)]
    for ad in ads:
        oss = ad["creative"]["object_story_spec"]
        L += [("Botão", ad["creative"].get("call_to_action_type"),
               ad["creative"].get("call_to_action_type") == "WATCH_LIVE_VIDEO"),
              ("Página do anúncio", oss.get("page_id"), oss.get("page_id") == p["pagina"]),
              ("Instagram", oss.get("instagram_user_id"), oss.get("instagram_user_id") == p["ig_user_id"]),
              ("Link", oss.get("link_data", {}).get("link"), oss.get("link_data", {}).get("link") == link_live(p)),
              ("Texto", oss.get("link_data", {}).get("message"), True),
              ("Status", f"{ad['status']} / {ad['effective_status']}", True)]
    if not ads:
        L.append(("Anúncio", "não existe", False))
    print(f"\n{'Item':<20} {'Valor':<58} OK")
    for k, v, ok in L:
        print(f"{k:<20} {str(v)[:58]:<58} {'✓' if ok else '✗'}")
    return all(ok for *_, ok in L)


def ligar(m, p, camp):
    if not live_no_ar(m, p):
        print("⚠️ Não achei live no ar. A Meta só revisa/entrega o anúncio com a live transmitindo.")
    for ad in m.get(f"{camp}/ads", fields="id")["data"]:
        m.post(ad["id"], status="ACTIVE")
    for s in m.get(f"{camp}/adsets", fields="id")["data"]:
        m.post(s["id"], status="ACTIVE")
    m.post(camp, status="ACTIVE")
    print("✓ Ligado (anúncio, conjunto e campanha). Revisão da Meta: em 30/09 levou ~3 min.")
    status(m, camp)


def status(m, camp):
    for ad in m.get(f"{camp}/ads", fields="effective_status,issues_info")["data"]:
        print(f"  anúncio: {ad['effective_status']} {ad.get('issues_info') or ''}")
    ins = m.get(f"{camp}/insights", fields="spend,reach,actions", date_preset="maximum").get("data", [])
    if ins:
        ac = {x["action_type"]: x["value"] for x in ins[0].get("actions", [])}
        print(f"  gasto R$ {ins[0]['spend']} · alcance {ins[0]['reach']} · "
              f"toques em Assistir {ac.get('link_click', 0)} · pararam 3s {ac.get('video_view', 0)}")


def relatorio(m, camp):
    """Números da live explicados: total, custo por toque, Stories × Reels, idade, sexo."""
    def acoes(linha):
        return {x["action_type"]: float(x["value"]) for x in linha.get("actions", [])}

    def tabela(titulo, quebra, rotulo):
        linhas = m.get(f"{camp}/insights", fields="spend,actions", breakdowns=quebra,
                       date_preset="maximum").get("data", [])
        total = sum(float(x["spend"]) for x in linhas) or 1
        print(f"\n{titulo}")
        print(f"  {'':<14}{'gasto':>13}{'% verba':>9}{'toques':>8}{'custo/toque':>14}")
        for x in sorted(linhas, key=lambda x: -float(x["spend"])):
            g, t = float(x["spend"]), acoes(x).get("link_click", 0)
            if g < 0.5:
                continue
            print(f"  {rotulo(x):<14}{brl(g):>13}{g / total:>9.0%}{int(t):>8}"
                  f"{(brl(g / t) if t else '-'):>14}")

    c = m.get(camp, fields="name")
    tot = m.get(f"{camp}/insights", date_preset="maximum", fields="spend,reach,impressions,cpm,actions,"
                "video_thruplay_watched_actions,video_avg_time_watched_actions").get("data", [])
    if not tot:
        print("Ainda sem números (a campanha não gastou).")
        return
    t = tot[0]
    ac = acoes(t)
    gasto, toques = float(t["spend"]), ac.get("link_click", 0)
    tp = float((t.get("video_thruplay_watched_actions") or [{}])[0].get("value", 0))
    tempo = (t.get("video_avg_time_watched_actions") or [{}])[0].get("value", "-")
    print(f"RELATÓRIO · {c['name']}")
    print(f"  Investido ...................... {brl(gasto)}")
    print(f"  Alcance (pessoas diferentes) ... {int(t['reach']):,}".replace(",", "."))
    print(f"  Custo por mil aparições ........ {brl(float(t.get('cpm', 0)))}")
    print(f"  Pararam 3 segundos ............. {int(ac.get('video_view', 0)):,}".replace(",", "."))
    print(f"  Assistiram 15s+ (ThruPlay) ..... {int(tp):,}".replace(",", ".")
          + (f"  ({brl(gasto / tp)} cada)" if tp else ""))
    print(f"  Tempo médio assistido .......... {tempo}s")
    print(f"  Tocaram em 'Assistir ao vivo' .. {int(toques):,}".replace(",", ".")
          + (f"  ({brl(gasto / toques)} cada)  ← número principal" if toques else ""))
    if t.get("reach") and toques:
        print(f"  Dos que viram, tocaram ......... {toques / float(t['reach']):.2%}".replace(".", ","))
    nomes = {"instagram_stories": "Stories", "instagram_reels": "Reels", "feed": "Feed"}
    tabela("ONDE APARECEU", "publisher_platform,platform_position",
           lambda x: nomes.get(x.get("platform_position"), x.get("platform_position")))
    tabela("IDADE", "age", lambda x: x.get("age"))
    tabela("SEXO", "gender", lambda x: {"female": "mulheres", "male": "homens"}.get(x.get("gender"), "não informado"))


def main():
    ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    ap.add_argument("acao", choices=["token", "configurar", "publicos", "checar", "plano", "criar", "auditar",
                                    "ligar", "status", "relatorio", "apagar"])
    ap.add_argument("campanha", nargs="?")
    ap.add_argument("--perfil", default="minha-conta")
    ap.add_argument("--token"); ap.add_argument("--conta"); ap.add_argument("--pagina")
    ap.add_argument("--nome", default=""); ap.add_argument("--texto"); ap.add_argument("--card")
    ap.add_argument("--publicos", help="IDs separados por vírgula (senão escolhe sozinho)")
    ap.add_argument("--pais", default="BR")
    ap.add_argument("--data"); ap.add_argument("--inicio"); ap.add_argument("--fim")
    ap.add_argument("--verba", type=float, default=1000.0)
    ap.add_argument("--criar", action="store_true", help="com publicos: cria os públicos quentes básicos")
    a = ap.parse_intermixed_args()

    if a.acao == "token":
        if not a.token:
            sys.exit("Cole o token: python3 live_nativo.py token --token \"EAA...\"")
        verificar_token(a.token)
        return
    if a.acao == "configurar":
        if not a.token:
            sys.exit("configurar precisa de --token (e de --conta se o token enxergar mais de uma conta)")
        return configurar(a)
    p = carregar_perfil(a.perfil)
    usar_conta(p)
    m = Meta(p["token"])
    if a.acao == "publicos":
        return cmd_publicos(m, p, a)
    if a.acao == "checar":
        sys.exit(0 if checar(m, p) else 1)
    if a.acao in ("plano", "criar"):
        if not a.fim:
            sys.exit("Informe --fim HH:MM (hora que a live termina).")
        a.executar = a.acao == "criar"
        return criar(m, p, a)
    if not a.campanha:
        sys.exit("Informe o ID da campanha.")
    if a.acao == "auditar":
        sys.exit(0 if auditar(m, p, a.campanha) else 1)
    if a.acao == "ligar":
        return ligar(m, p, a.campanha)
    if a.acao == "relatorio":
        return relatorio(m, a.campanha)
    if a.acao == "status":
        return status(m, a.campanha)
    if a.acao == "apagar":
        m.post(a.campanha, status="DELETED")
        print(f"✓ campanha {a.campanha} apagada")


if __name__ == "__main__":
    try:
        main()
    except ApiError as e:
        sys.exit(f"✗ Meta recusou: {e}")
