#!/usr/bin/env python3
"""
Interface Web - Prospecção Ativa via Google Maps
Flask app rodando na porta 5050
"""

import sys
import os
sys.path.insert(0, "/opt/mia/workspace/prospeccao_ativa")

import uuid
import time
import threading
import requests
import re
import json
import csv
import io
from datetime import datetime
from pathlib import Path
from flask import Flask, jsonify, render_template, request, redirect, url_for, flash
from flask_login import LoginManager, login_user, logout_user, login_required, current_user
from auth import (init_db, buscar_por_email, criar_usuario, listar_usuarios,
                  atualizar_usuario, deletar_usuario, verificar_senha, registrar_acesso, User,
                  get_ghl_config, save_ghl_config,
                  criar_conta, listar_contas, get_conta, deletar_conta, renomear_conta,
                  get_quota, incrementar_quota, resetar_quota_se_novo_mes)

app = Flask(__name__)
app.secret_key = "prospeccao-climb-2026-secret"

login_manager = LoginManager(app)
login_manager.login_view = "login"
login_manager.login_message = "Faça login para acessar a ferramenta."
login_manager.login_message_category = "error"

@login_manager.user_loader
def load_user(user_id):
    return User.get(user_id)

# Armazenamento em memória dos jobs
jobs = {}
MAX_JOBS_HISTORY = 10

# Registro persistente de empresas já prospectadas
REGISTRO_PATH = Path("/opt/mia/workspace/prospeccao_ativa/prospectados.json")

def _carregar_registro() -> set:
    try:
        if REGISTRO_PATH.exists():
            return set(json.loads(REGISTRO_PATH.read_text()))
    except Exception:
        pass
    return set()

def _salvar_registro(registro: set):
    try:
        REGISTRO_PATH.write_text(json.dumps(list(registro)))
    except Exception:
        pass

def _chave_empresa(nome: str, cidade: str) -> str:
    return f"{nome.lower().strip()}|{cidade.lower().strip()}"

GHL_BASE = "https://services.leadconnectorhq.com"
UNNICHAT_BASE = "https://unnichat.com.br"


def _ghl_headers(token: str) -> dict:
    return {
        "Authorization": f"Bearer {token}",
        "Content-Type": "application/json",
        "Version": "2021-07-28"
    }


# ── UNNICHAT ──────────────────────────────────────────

def unnichat_adicionar_pipeline(contact_id: str, token: str, connection_id: str,
                                pipeline_id: str, column_id: str, business_name: str) -> tuple:
    """Adiciona contato a uma coluna do pipeline no Unnichat."""
    headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
    if connection_id:
        headers["x-connection-id"] = connection_id
    payload = {
        "crm_id": pipeline_id,
        "column_id": column_id,
        "business_name": business_name,
    }
    try:
        r = requests.post(
            f"{UNNICHAT_BASE}/api/contact/{contact_id}/crm",
            headers=headers, json=payload, timeout=15
        )
        if r.status_code in (200, 201):
            return True, None
        try:
            msg = r.json().get("message") or r.text[:120]
        except Exception:
            msg = r.text[:120]
        return False, f"HTTP {r.status_code}: {msg}"
    except Exception as e:
        return False, str(e)


def unnichat_criar_contato(lead: dict, token: str, tags: list, connection_id: str = "") -> tuple:
    """Cria contato no Unnichat. Retorna (contact_id, erro)."""
    telefone = re.sub(r"[^\d+]", "", lead.get("telefone", ""))
    payload = {
        "name": lead["nome"],
        "phone": telefone or None,
        "email": lead.get("email") or None,
        "tags": tags or ["prospeccao-ativa"],
    }
    payload = {k: v for k, v in payload.items() if v is not None}
    headers = {"Authorization": f"Bearer {token}", "Content-Type": "application/json"}
    if connection_id:
        headers["x-connection-id"] = connection_id
    try:
        r = requests.post(
            f"{UNNICHAT_BASE}/api/contact",
            headers=headers,
            json=payload,
            timeout=15
        )
        if r.status_code in (200, 201):
            data = r.json().get("data", {})
            return data.get("id"), None
        try:
            msg = r.json().get("message") or r.text[:120]
        except Exception:
            msg = r.text[:120]
        return None, f"HTTP {r.status_code}: {msg}"
    except Exception as e:
        return None, str(e)


def buscar_contato_existente_ghl(telefone: str, nome: str, token: str, location_id: str) -> str | None:
    """Busca contato existente por telefone ou nome."""
    headers = _ghl_headers(token)

    # Tenta por telefone primeiro (mais preciso)
    if telefone:
        try:
            r = requests.get(
                f"{GHL_BASE}/contacts/",
                headers=headers,
                params={"locationId": location_id, "query": telefone, "limit": 1},
                timeout=10
            )
            if r.status_code == 200:
                contacts = r.json().get("contacts", [])
                if contacts:
                    return contacts[0].get("id")
        except Exception:
            pass

    # Tenta por nome
    try:
        r = requests.get(
            f"{GHL_BASE}/contacts/",
            headers=headers,
            params={"locationId": location_id, "query": nome[:50], "limit": 5},
            timeout=10
        )
        if r.status_code == 200:
            contacts = r.json().get("contacts", [])
            # Busca correspondência exata por companyName
            for c in contacts:
                if (c.get("companyName") or "").lower() == nome.lower():
                    return c.get("id")
            # Se não encontrou exato, retorna o primeiro
            if contacts:
                return contacts[0].get("id")
    except Exception:
        pass

    return None


def criar_contato_ghl(lead: dict, token: str, location_id: str, source: str = "Google Maps") -> str | None:
    """Cria contato no CRM e retorna o contact ID. Se duplicado, retorna o ID existente."""
    nome_split = lead["nome"].split(" ", 1)
    primeiro = nome_split[0]
    sobrenome = nome_split[1] if len(nome_split) > 1 else ""

    telefone = re.sub(r"[^\d+]", "", lead.get("telefone", ""))
    tags = lead.get("tags") or ["prospeccao-ativa", lead.get("nicho", ""), lead.get("cidade", "")]
    tags = [t for t in tags if t]

    payload = {
        "locationId": location_id,
        "firstName": primeiro,
        "lastName": sobrenome,
        "companyName": lead.get("empresa") or lead["nome"],
        "phone": telefone or None,
        "email": lead.get("email") or None,
        "website": lead.get("site") or None,
        "address1": lead.get("endereco") or None,
        "tags": tags,
        "source": source,
    }
    payload = {k: v for k, v in payload.items() if v is not None}

    r = requests.post(
        f"{GHL_BASE}/contacts/",
        headers=_ghl_headers(token),
        json=payload,
        timeout=15
    )

    if r.status_code in (200, 201):
        return r.json().get("contact", {}).get("id"), None

    # Se duplicado, busca o contato existente e usa o ID dele
    try:
        msg = r.json().get("message") or r.json().get("msg") or ""
    except Exception:
        msg = r.text[:200]

    if r.status_code == 400 and "duplicate" in msg.lower():
        contact_id = buscar_contato_existente_ghl(telefone, lead["nome"], token, location_id)
        if contact_id:
            return contact_id, None
        return None, f"Duplicado mas não encontrado na busca"

    return None, f"HTTP {r.status_code}: {msg[:120]}"


def criar_oportunidade_ghl(lead: dict, contact_id: str, token: str, location_id: str,
                           pipeline_id: str, stage_id: str, source: str = "Google Maps") -> tuple:
    """Cria oportunidade no pipeline. Retorna (ok, opportunity_id, erro)."""
    payload = {
        "pipelineId": pipeline_id,
        "locationId": location_id,
        "name": lead.get("empresa") or lead["nome"],
        "pipelineStageId": stage_id,
        "status": "open",
        "contactId": contact_id,
        "source": source,
    }

    r = requests.post(
        f"{GHL_BASE}/opportunities/",
        headers=_ghl_headers(token),
        json=payload,
        timeout=15
    )
    if r.status_code in (200, 201):
        opp_id = r.json().get("opportunity", {}).get("id") or r.json().get("id")
        return True, opp_id, None
    try:
        erro = r.json().get("message") or r.json().get("msg") or str(r.json())
    except Exception:
        erro = r.text[:120]
    return False, None, f"HTTP {r.status_code}: {erro}"


def run_prospector(job_id: str, nicho: str, cidade: str, limite: int,
                   token: str, location_id: str, pipeline_id: str, stage_id: str,
                   crm_type: str = "ghl", connection_id: str = "",
                   pais: str = "BR", enriquecimento_auto: bool = False,
                   conta_id: int = None):
    """Executa o scraper em thread separada com callbacks de progresso."""
    job = jobs[job_id]
    job["status"] = "em_andamento"
    job["pais"] = pais
    job["conta_id"] = conta_id
    preposicao = "in" if pais == "US" else "em"
    job["logs"].append(f"[{_ts()}] Iniciando busca: {nicho} {preposicao} {cidade} (limite: {limite})")

    try:
        leads_coletados = []

        def scrape_com_progresso():
            try:
                from config import GOOGLE_PLACES_KEY
            except ImportError:
                GOOGLE_PLACES_KEY = ""

            if GOOGLE_PLACES_KEY:
                return _buscar_via_places_api(GOOGLE_PLACES_KEY)
            else:
                return _buscar_via_playwright()

        def _buscar_via_places_api(api_key: str) -> list:
            """Busca empresas via Google Places API (New) — oficial, sem risco de bloqueio."""
            PLACES_URL = "https://places.googleapis.com/v1/places:searchText"
            FIELD_MASK = "places.displayName,places.formattedAddress,places.nationalPhoneNumber,places.websiteUri,places.rating,places.userRatingCount,places.id"

            preposicao_q = "in" if pais == "US" else "em"
            query = f"{nicho} {preposicao_q} {cidade}"
            job["logs"].append(f"[{_ts()}] Google Places API: buscando '{query}'...")

            leads = []
            registro = _carregar_registro()
            pulados = 0
            page_token = None
            coletados = 0

            while coletados < limite:
                if job["status"] in ("cancelado", "pausado"):
                    break

                body = {"textQuery": query, "maxResultCount": min(20, limite - coletados)}
                if page_token:
                    body["pageToken"] = page_token

                try:
                    r = requests.post(
                        PLACES_URL,
                        headers={
                            "Content-Type": "application/json",
                            "X-Goog-Api-Key": api_key,
                            "X-Goog-FieldMask": FIELD_MASK,
                        },
                        json=body,
                        timeout=15
                    )
                    if r.status_code != 200:
                        job["logs"].append(f"[{_ts()}] Places API erro: {r.status_code} {r.text[:100]}")
                        break

                    data = r.json()
                    places = data.get("places", [])
                    if not places:
                        break

                    job["total"] = min(limite, job["total"] + len(places))

                    for place in places:
                        if job["status"] in ("cancelado", "pausado") or coletados >= limite:
                            break

                        nome = place.get("displayName", {}).get("text", "").strip()
                        if not nome or len(nome) < 2:
                            continue

                        chave = _chave_empresa(nome, cidade)
                        if chave in registro:
                            pulados += 1
                            continue

                        lead = {
                            "nome": nome,
                            "telefone": place.get("nationalPhoneNumber", "").strip(),
                            "site": place.get("websiteUri", "").strip(),
                            "endereco": place.get("formattedAddress", "").strip(),
                            "avaliacao": str(place.get("rating", "")),
                            "nicho": nicho,
                            "cidade": cidade,
                            "enviado_crm": False,
                            "contact_id": None,
                        }

                        registro.add(chave)
                        _salvar_registro(registro)
                        leads.append(lead)
                        coletados += 1
                        job["progresso"] = coletados
                        job["leads"].append(lead)
                        job["logs"].append(
                            f"[{_ts()}] Coletado ({coletados}): {nome} | {lead['telefone'] or 'sem tel'}"
                        )
                        if len(job["logs"]) > 50:
                            job["logs"] = job["logs"][-50:]

                    page_token = data.get("nextPageToken")
                    if not page_token:
                        break

                    time.sleep(2)

                except Exception as e:
                    job["logs"].append(f"[{_ts()}] Places API exceção: {str(e)[:100]}")
                    break

            if pulados > 0:
                job["logs"].append(f"[{_ts()}] {pulados} empresa(s) puladas (já prospectadas antes)")

            return leads

        def _buscar_via_playwright() -> list:
            """Fallback: busca via scraping do Google Maps com Playwright."""
            from playwright.sync_api import sync_playwright

            leads = []
            preposicao_q = "in" if pais == "US" else "em"
            query = f"{nicho} {preposicao_q} {cidade}"
            lang_param = "hl=en&gl=us" if pais == "US" else "hl=pt-BR&gl=br"

            with sync_playwright() as p:
                browser = p.chromium.launch(headless=True)
                page = browser.new_page()

                job["logs"].append(f"[{_ts()}] Buscando (scraping): {query}")
                page.goto(f"https://www.google.com/maps/search/{query.replace(' ', '+')}?{lang_param}")
                page.wait_for_timeout(3000)

                if job["status"] == "cancelado":
                    browser.close()
                    return leads

                scrolls = min(limite // 5, 20)
                for i in range(scrolls):
                    if job["status"] == "cancelado":
                        break
                    try:
                        panel = page.locator('div[role="feed"]').first
                        panel.evaluate("el => el.scrollTop += 1500")
                        page.wait_for_timeout(1500)
                    except Exception:
                        break

                cards = page.locator('a[href*="/maps/place/"]').all()
                total_cards = min(len(cards), limite)
                job["total"] = total_cards
                job["logs"].append(f"[{_ts()}] {total_cards} resultados, coletando...")

                registro = _carregar_registro()
                pulados = 0

                for idx, card in enumerate(cards[:limite]):
                    if job["status"] in ("cancelado", "pausado"):
                        break
                    try:
                        lead = {}
                        try:
                            aria = card.get_attribute("aria-label", timeout=1000) or ""
                            lead["nome"] = aria.strip().split('\n')[0] if aria else card.inner_text(timeout=1000).strip().split('\n')[0]
                        except Exception:
                            continue

                        if not lead["nome"] or len(lead["nome"]) < 2:
                            continue

                        chave = _chave_empresa(lead["nome"], cidade)
                        if chave in registro:
                            pulados += 1
                            continue

                        card.click()
                        page.wait_for_timeout(2000)

                        for seletor, campo, transform in [
                            ('[data-item-id^="phone"]', "telefone", lambda v: v.replace("phone:tel:", "").strip()),
                            ('[data-item-id="authority"]', "site", lambda v: v or ""),
                            ('[data-item-id^="address"]', "endereco", lambda v: v),
                        ]:
                            try:
                                el = page.locator(seletor).first
                                attr = el.get_attribute("data-item-id" if "phone" in seletor or "authority" in seletor else "data-item-id", timeout=2000)
                                if campo == "site":
                                    lead[campo] = el.get_attribute("href", timeout=2000) or ""
                                elif campo == "endereco":
                                    lead[campo] = el.inner_text(timeout=2000).strip()
                                else:
                                    lead[campo] = transform(attr or "")
                            except Exception:
                                lead[campo] = ""

                        lead.setdefault("avaliacao", "")
                        lead["nicho"] = nicho
                        lead["cidade"] = cidade
                        lead["enviado_crm"] = False
                        lead["contact_id"] = None

                        registro.add(chave)
                        _salvar_registro(registro)
                        leads.append(lead)
                        job["progresso"] = len(leads)
                        job["leads"].append(lead)
                        job["logs"].append(f"[{_ts()}] Coletado ({idx+1}/{total_cards}): {lead['nome']} | {lead.get('telefone') or 'sem tel'}")
                        if len(job["logs"]) > 50:
                            job["logs"] = job["logs"][-50:]

                    except Exception as e:
                        job["logs"].append(f"[{_ts()}] Aviso card {idx+1}: {str(e)[:80]}")
                        continue

                if pulados > 0:
                    job["logs"].append(f"[{_ts()}] {pulados} empresa(s) puladas (já prospectadas antes)")
                browser.close()

            return leads

        leads_coletados = scrape_com_progresso()

        if job["status"] == "cancelado":
            job["logs"].append(f"[{_ts()}] Job cancelado pelo usuario")
            return

        if job["status"] == "pausado":
            job["logs"].append(f"[{_ts()}] Pausado pelo usuario - enviando {len(leads_coletados)} leads coletados ao CRM...")

        if not leads_coletados:
            job["status"] = "concluido"
            job["logs"].append(f"[{_ts()}] Nenhum lead encontrado")
            return

        job["logs"].append(f"[{_ts()}] {len(leads_coletados)} leads coletados. Enviando ao CRM...")

        # ── Enriquecimento automático (BR: CNPJ/Receita | US: site + DuckDuckGo) ──
        if enriquecimento_auto:
            job["logs"].append(f"[{_ts()}] Enriquecendo leads ({pais}) antes de enviar ao CRM...")
            for i, lead in enumerate(leads_coletados):
                try:
                    if pais == "US":
                        dados = enriquecer_empresa_us(
                            lead["nome"],
                            website=lead.get("site", ""),
                            cidade=cidade
                        )
                    else:
                        dados = enriquecer_empresa(lead["nome"])

                    if dados.get("telefone") and not lead.get("telefone"):
                        lead["telefone"] = dados["telefone"]
                    if dados.get("email") and not lead.get("email"):
                        lead["email"] = dados["email"]
                    if dados.get("cnpj"):
                        lead["cnpj"] = dados["cnpj"]
                    if dados.get("decisor_nome"):
                        lead["decisor_nome"] = dados["decisor_nome"]
                        lead["decisor_cargo"] = dados.get("decisor_cargo", "")
                    if dados.get("linkedin_url"):
                        lead["linkedin_url"] = dados["linkedin_url"]
                    fontes = ", ".join(dados.get("fontes", []))
                    job["logs"].append(f"[{_ts()}] Enriq ({i+1}/{len(leads_coletados)}): {lead['nome']} [{fontes or 'sem dados extras'}]")
                except Exception as e:
                    job["logs"].append(f"[{_ts()}] Enriq erro para {lead['nome']}: {e}")
            job["logs"].append(f"[{_ts()}] Enriquecimento concluído. Enviando ao CRM...")

        enviados = 0
        job["status"] = "em_andamento"
        for i, lead in enumerate(leads_coletados):
            if job["status"] == "cancelado":
                break

            if crm_type == "unnichat":
                # ── Unnichat: cria contato + adiciona ao pipeline ──
                contact_id, err_contato = unnichat_criar_contato(
                    lead, token, ["prospeccao-ativa", lead.get("nicho", ""), lead.get("cidade", "")],
                    connection_id=connection_id
                )
                if contact_id:
                    # Adiciona ao pipeline AGENTE SDR / FILA DE ATENDIMENTO
                    pip_ok, pip_err = unnichat_adicionar_pipeline(
                        contact_id, token, connection_id,
                        pipeline_id=pipeline_id or "8TgKxh0VK37bGNkbN8yO",
                        column_id=stage_id or "HThNTr24Yy2fN0vmeLm2",
                        business_name=lead["nome"]
                    )
                    lead["enviado_crm"] = True
                    lead["contact_id"] = contact_id
                    enviados += 1
                    job["leads_enviados"] = enviados
                    status_pip = "+ pipeline" if pip_ok else f"(pipeline: {pip_err})"
                    job["logs"].append(
                        f"[{_ts()}] Unnichat ({i+1}/{len(leads_coletados)}): {lead['nome']} enviado {status_pip}"
                    )
                else:
                    job["logs"].append(
                        f"[{_ts()}] Unnichat: falhou para {lead['nome']} | {err_contato}"
                    )
            else:
                # ── CRM: cria contato + oportunidade no pipeline ──
                contact_id, err_contato = criar_contato_ghl(lead, token, location_id)
                if contact_id:
                    ok, opportunity_id, err_oport = criar_oportunidade_ghl(lead, contact_id, token, location_id, pipeline_id, stage_id)
                    is_duplicate = err_oport and "duplicate" in err_oport.lower()
                    if ok or is_duplicate:
                        lead["enviado_crm"] = True
                        lead["contact_id"] = contact_id
                        enviados += 1
                        job["leads_enviados"] = enviados
                        status_msg = "já existe no pipeline" if is_duplicate else "enviado"
                        job["logs"].append(
                            f"[{_ts()}] CRM ({i+1}/{len(leads_coletados)}): {lead['nome']} {status_msg}"
                        )
                        # ── Popula campos enriquecidos no CRM ──
                        if enriquecimento_auto and any(lead.get(k) for k in ("email", "cnpj", "decisor_nome", "linkedin_url")):
                            try:
                                _adicionar_nota_ghl(contact_id, token, location_id, lead, opportunity_id=opportunity_id)
                                job["logs"].append(
                                    f"[{_ts()}] Campos enriquecidos salvos: {lead['nome']}"
                                )
                            except Exception as e:
                                job["logs"].append(f"[{_ts()}] Aviso: erro ao salvar campos de {lead['nome']}: {e}")
                    else:
                        job["logs"].append(
                            f"[{_ts()}] CRM: oportunidade falhou para {lead['nome']} | {err_oport}"
                        )
                else:
                    job["logs"].append(
                        f"[{_ts()}] CRM: contato falhou para {lead['nome']} | {err_contato}"
                    )

            if len(job["logs"]) > 50:
                job["logs"] = job["logs"][-50:]

            time.sleep(0.5)

        job["leads_enviados"] = enviados
        job["status"] = "concluido"
        job["logs"].append(
            f"[{_ts()}] Concluido: {enviados}/{len(leads_coletados)} leads enviados ao CRM"
        )
        job["concluido_em"] = datetime.now().isoformat()

        # Incrementa quota mensal da conta (total de leads coletados pela ferramenta)
        try:
            if conta_id and len(leads_coletados) > 0:
                resetar_quota_se_novo_mes(conta_id)
                quota = incrementar_quota(conta_id, len(leads_coletados))
                job["logs"].append(
                    f"[{_ts()}] Quota: {quota['usados']}/{quota['limite']} ({quota['disponivel']} disponiveis)"
                )
        except Exception as e:
            job["logs"].append(f"[{_ts()}] Aviso quota: {str(e)[:80]}")

    except Exception as e:
        job["status"] = "erro"
        job["erro"] = str(e)
        job["logs"].append(f"[{_ts()}] ERRO: {str(e)}")


def _ts():
    return datetime.now().strftime("%H:%M:%S")


def _novo_job(nicho, cidade, limite):
    job_id = str(uuid.uuid4())[:8]
    jobs[job_id] = {
        "id": job_id,
        "tipo": "prospeccao",
        "nicho": nicho,
        "cidade": cidade,
        "limite": limite,
        "status": "aguardando",
        "progresso": 0,
        "total": limite,
        "leads_enviados": 0,
        "leads": [],
        "logs": [],
        "erro": None,
        "criado_em": datetime.now().isoformat(),
        "concluido_em": None,
    }
    if len(jobs) > MAX_JOBS_HISTORY:
        ids_ordenados = sorted(jobs.keys(), key=lambda k: jobs[k]["criado_em"])
        for old_id in ids_ordenados[:-MAX_JOBS_HISTORY]:
            del jobs[old_id]
    return job_id


def _novo_job_enriq(empresas: list) -> str:
    job_id = str(uuid.uuid4())[:8]
    jobs[job_id] = {
        "id": job_id,
        "tipo": "enriquecimento",
        "empresas": empresas,
        "status": "aguardando",
        "progresso": 0,
        "total": len(empresas),
        "leads_enviados": 0,
        "logs": [],
        "leads": [],
        "erro": None,
        "criado_em": datetime.now().isoformat(),
        "concluido_em": None,
    }
    if len(jobs) > MAX_JOBS_HISTORY:
        ids_ordenados = sorted(jobs.keys(), key=lambda k: jobs[k]["criado_em"])
        for old_id in ids_ordenados[:-MAX_JOBS_HISTORY]:
            del jobs[old_id]
    return job_id


# ── ENRIQUECIMENTO ────────────────────────────────────────────────────────────

def enriquecer_empresa(nome_empresa: str) -> dict:
    """
    Dado o nome da empresa:
    1. Busca CNPJ via DuckDuckGo (extrai de sites de consulta)
    2. Consulta ReceitaWS com o CNPJ -> telefone, email, endereço, sócios (Receita Federal)
    3. Busca LinkedIn do decisor via DuckDuckGo
    """
    from ddgs import DDGS

    dados: dict = {
        "nome": nome_empresa,
        "cnpj": "",
        "telefone": "",
        "whatsapp": "",
        "email": "",
        "site": "",
        "decisor_nome": "",
        "decisor_cargo": "",
        "linkedin_url": "",
        "endereco": "",
        "fontes": [],
    }

    # --- 1. Busca CNPJ via DuckDuckGo (região Brasil) ---
    cnpj_raw = ""
    try:
        with DDGS() as ddgs:
            resultados = list(ddgs.text(f"{nome_empresa} CNPJ", max_results=5, region="br-pt"))
        for r in resultados:
            texto = r.get("body", "") + " " + r.get("href", "") + " " + r.get("title", "")
            matches = re.findall(r"\d{2}\.?\d{3}\.?\d{3}\/?\d{4}-?\d{2}", texto)
            for match in matches:
                limpo = re.sub(r"[^\d]", "", match)
                if len(limpo) == 14 and len(set(limpo)) > 3:
                    cnpj_raw = match
                    break
            if cnpj_raw:
                break
        time.sleep(1)
    except Exception:
        pass

    # --- 2. Consulta ReceitaWS (dados da Receita Federal) ---
    if cnpj_raw:
        cnpj_limpo = re.sub(r"[^\d]", "", cnpj_raw)
        if len(cnpj_limpo) == 14:
            try:
                r_api = requests.get(
                    f"https://receitaws.com.br/v1/cnpj/{cnpj_limpo}",
                    headers={"User-Agent": "Mozilla/5.0"},
                    timeout=12
                )
                if r_api.status_code == 200:
                    api_data = r_api.json()
                    if api_data.get("status") != "ERROR":
                        tel = api_data.get("telefone", "").strip()
                        if tel:
                            dados["telefone"] = tel
                            dados["whatsapp"] = tel
                        dados["email"] = (api_data.get("email", "") or "").strip()
                        # Endereço
                        partes = [p for p in [
                            api_data.get("logradouro", ""),
                            api_data.get("numero", ""),
                            api_data.get("bairro", ""),
                            api_data.get("municipio", ""),
                            api_data.get("uf", ""),
                        ] if p]
                        dados["endereco"] = ", ".join(partes)
                        # Formata CNPJ
                        c = cnpj_limpo
                        dados["cnpj"] = f"{c[:2]}.{c[2:5]}.{c[5:8]}/{c[8:12]}-{c[12:14]}"
                        # Sócio principal (decisor)
                        qsa = api_data.get("qsa", [])
                        if qsa:
                            dados["decisor_nome"] = qsa[0].get("nome", "").strip()
                            dados["decisor_cargo"] = qsa[0].get("qual", "").strip()
                        dados["fontes"].append("receita_federal")
                time.sleep(1)
            except Exception:
                pass

    # --- 3. Busca LinkedIn via DuckDuckGo ---
    try:
        with DDGS() as ddgs:
            li_results = list(ddgs.text(
                f'site:linkedin.com/in "{nome_empresa}"',
                max_results=3,
                region="br-pt"
            ))
        for r in li_results:
            url = r.get("href", "")
            if "linkedin.com/in/" in url:
                dados["linkedin_url"] = url.split("?")[0]
                dados["fontes"].append("linkedin")
                break
        time.sleep(1)
    except Exception:
        pass

    return dados


def enriquecer_empresa_us(nome_empresa: str, website: str = "", cidade: str = "") -> dict:
    """
    Enriquecimento para empresas nos EUA (sem CNPJ):
    1. Scraping do site da empresa: emails, telefones, nome do responsável
    2. DuckDuckGo: decisor (owner/CEO/founder) + LinkedIn
    """
    from ddgs import DDGS
    from bs4 import BeautifulSoup

    dados: dict = {
        "nome": nome_empresa,
        "cnpj": "",
        "telefone": "",
        "email": "",
        "site": website or "",
        "decisor_nome": "",
        "decisor_cargo": "",
        "linkedin_url": "",
        "endereco": "",
        "fontes": [],
    }

    headers_req = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120 Safari/537.36",
        "Accept-Language": "en-US,en;q=0.9",
    }

    # --- 1. Scraping do site ---
    site_url = website.strip() if website else ""
    if site_url and not site_url.startswith("http"):
        site_url = "https://" + site_url

    if site_url:
        pages_to_try = [site_url]
        for suffix in ["/contact", "/contact-us", "/about", "/about-us", "/team"]:
            pages_to_try.append(site_url.rstrip("/") + suffix)

        found_email = ""
        found_phone = ""
        found_person = ""

        for page_url in pages_to_try[:4]:
            try:
                resp = requests.get(page_url, headers=headers_req, timeout=8, allow_redirects=True)
                if resp.status_code != 200:
                    continue
                soup = BeautifulSoup(resp.text, "html.parser")
                text = soup.get_text(" ", strip=True)

                # Extrai email
                if not found_email:
                    emails = re.findall(r"[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}", text)
                    for em in emails:
                        if not any(x in em.lower() for x in ["example", "youremail", "test", "noreply", "info@", "hello@"]):
                            found_email = em
                            break
                    if not found_email and emails:
                        found_email = emails[0]

                # Extrai telefone
                if not found_phone:
                    phones = re.findall(r"(?:\+1[-.\s]?)?\(?\d{3}\)?[-.\s]\d{3}[-.\s]\d{4}", text)
                    if phones:
                        found_phone = phones[0]

                # Extrai nome de pessoa (Owner/CEO/Founder/President)
                if not found_person:
                    match = re.search(
                        r"([A-Z][a-z]+ [A-Z][a-z]+(?:\s[A-Z][a-z]+)?)\s*[,\-–|]?\s*"
                        r"(Owner|Co-Owner|CEO|Founder|Co-Founder|President|Director|Manager|Principal)",
                        text
                    )
                    if match:
                        found_person = match.group(1).strip()
                        dados["decisor_cargo"] = match.group(2).strip()

                if found_email and found_phone:
                    break
                time.sleep(0.5)

            except Exception:
                continue

        if found_email:
            dados["email"] = found_email
            dados["fontes"].append("website_scraping")
        if found_phone and not dados.get("telefone"):
            dados["telefone"] = found_phone
            if "website_scraping" not in dados["fontes"]:
                dados["fontes"].append("website_scraping")
        if found_person:
            dados["decisor_nome"] = found_person

    # --- 2. DuckDuckGo: decisor ---
    if not dados["decisor_nome"]:
        try:
            query = f'"{nome_empresa}" {cidade} owner OR CEO OR founder'.strip()
            with DDGS() as ddgs:
                results = list(ddgs.text(query, max_results=5, region="us-en"))
            for r in results:
                snippet = r.get("body", "") + " " + r.get("title", "")
                m = re.search(
                    r"([A-Z][a-z]+ [A-Z][a-z]+)\s*[,\-–]?\s*(owner|CEO|founder|president|director|manager)",
                    snippet, re.IGNORECASE
                )
                if m:
                    dados["decisor_nome"] = m.group(1).strip()
                    dados["decisor_cargo"] = m.group(2).strip().title()
                    dados["fontes"].append("duckduckgo")
                    break
            time.sleep(1)
        except Exception:
            pass

    # --- 3. DuckDuckGo: LinkedIn ---
    try:
        li_query = f'site:linkedin.com/in "{nome_empresa}" owner OR CEO OR founder'
        with DDGS() as ddgs:
            li_results = list(ddgs.text(li_query, max_results=3, region="us-en"))
        for r in li_results:
            url = r.get("href", "")
            if "linkedin.com/in/" in url:
                dados["linkedin_url"] = url.split("?")[0]
                if "linkedin" not in dados["fontes"]:
                    dados["fontes"].append("linkedin")
                # Tenta extrair nome do título do resultado
                if not dados["decisor_nome"]:
                    title = r.get("title", "")
                    m = re.match(r"^([A-Z][a-z]+ [A-Z][a-z]+)", title)
                    if m:
                        dados["decisor_nome"] = m.group(1)
                break
        time.sleep(1)
    except Exception:
        pass

    # --- 4. DuckDuckGo: email (fallback) ---
    if not dados["email"]:
        try:
            eq = f'"{nome_empresa}" {cidade} email contact'.strip()
            with DDGS() as ddgs:
                eq_results = list(ddgs.text(eq, max_results=5, region="us-en"))
            for r in eq_results:
                text = r.get("body", "") + " " + r.get("title", "")
                emails = re.findall(r"[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}", text)
                for em in emails:
                    if not any(x in em.lower() for x in ["example", "youremail", "test"]):
                        dados["email"] = em
                        dados["fontes"].append("duckduckgo_email")
                        break
                if dados["email"]:
                    break
            time.sleep(1)
        except Exception:
            pass

    return dados


def _adicionar_nota_ghl(contact_id: str, token: str, location_id: str, dados: dict, opportunity_id: str = None):
    """Popula campos personalizados na oportunidade + adiciona nota."""

    # 1. Popula campos personalizados na OPORTUNIDADE (aparece no card do pipeline)
    campos_ids = _carregar_campos_ghl()
    custom_fields = []
    mapa = {
        "CNPJ":             dados.get("cnpj", ""),
        "Email":            dados.get("email", ""),
        "LinkedIn":         dados.get("linkedin_url", ""),
        "Decisor":          dados.get("decisor_nome", ""),
        "Cargo do Decisor": dados.get("decisor_cargo", ""),
    }
    for nome_campo, valor in mapa.items():
        field_id = campos_ids.get(nome_campo)
        if field_id and valor:
            custom_fields.append({"id": field_id, "field_value": valor})

    # Salva na OPORTUNIDADE (campos são opportunity-level no GHL)
    if custom_fields and opportunity_id:
        try:
            requests.put(
                f"{GHL_BASE}/opportunities/{opportunity_id}",
                headers=_ghl_headers(token),
                json={"customFields": custom_fields},
                timeout=10
            )
        except Exception:
            pass

    # 2. Adiciona nota com resumo completo
    linhas = []
    if dados.get("cnpj"):         linhas.append(f"CNPJ: {dados['cnpj']}")
    if dados.get("email"):        linhas.append(f"Email: {dados['email']}")
    if dados.get("decisor_nome"): linhas.append(f"Decisor: {dados['decisor_nome']} ({dados.get('decisor_cargo', '')})")
    if dados.get("linkedin_url"): linhas.append(f"LinkedIn: {dados['linkedin_url']}")
    if dados.get("endereco"):     linhas.append(f"Endereço: {dados['endereco']}")

    if linhas:
        try:
            requests.post(
                f"{GHL_BASE}/contacts/{contact_id}/notes",
                headers=_ghl_headers(token),
                json={"body": "\n".join(linhas), "userId": location_id},
                timeout=10
            )
        except Exception:
            pass


def run_enriquecedor(job_id: str, empresas: list, token: str,
                     location_id: str, pipeline_id: str, stage_id: str):
    """Enriquece lista de empresas e cria contatos no CRM."""
    job = jobs[job_id]
    job["status"] = "em_andamento"
    job["total"] = len(empresas)

    try:
        for i, nome_empresa in enumerate(empresas):
            if job["status"] == "cancelado":
                break

            job["logs"].append(f"[{_ts()}] Enriquecendo ({i+1}/{len(empresas)}): {nome_empresa}")
            if len(job["logs"]) > 100:
                job["logs"] = job["logs"][-100:]

            dados = enriquecer_empresa(nome_empresa)

            # Monta lead no formato que criar_contato_ghl espera
            lead = {
                "nome": dados.get("decisor_nome") or nome_empresa,
                "telefone": dados.get("telefone", ""),
                "email": dados.get("email", ""),
                "empresa": nome_empresa,
                "endereco": dados.get("endereco", ""),
                "site": dados.get("site", ""),
            }

            tags = ["lista-enriquecida", "enriquecedor"]
            if dados.get("linkedin_url"):
                tags.append("linkedin-encontrado")
            if dados.get("cnpj"):
                tags.append("cnpj-encontrado")
            lead["tags"] = tags

            contact_id, err = criar_contato_ghl(lead, token, location_id, source="Enriquecimento")
            if contact_id:
                opportunity_id = None
                if pipeline_id and stage_id:
                    ok, opportunity_id, _ = criar_oportunidade_ghl(
                        lead, contact_id, token, location_id, pipeline_id, stage_id, source="Enriquecimento"
                    )
                _adicionar_nota_ghl(contact_id, token, location_id, dados, opportunity_id=opportunity_id)
                job["leads_enviados"] += 1
                job["logs"].append(
                    f"[{_ts()}] ✓ {nome_empresa} → CRM "
                    f"(fone: {dados.get('telefone') or '—'} | "
                    f"LinkedIn: {'sim' if dados.get('linkedin_url') else 'não'})"
                )
            else:
                job["logs"].append(f"[{_ts()}] ✗ {nome_empresa} → erro CRM: {err}")

            job["progresso"] = i + 1
            job["leads"].append(dados)
            time.sleep(1)

        job["status"] = "concluido"
        job["concluido_em"] = datetime.now().isoformat()
        job["logs"].append(
            f"[{_ts()}] Concluído: {job['leads_enviados']}/{len(empresas)} enviados ao CRM"
        )

    except Exception as e:
        job["status"] = "erro"
        job["erro"] = str(e)
        job["logs"].append(f"[{_ts()}] ERRO: {str(e)}")


# ---------- ROUTES ----------

@app.route("/login", methods=["GET", "POST"])
def login():
    if current_user.is_authenticated:
        return redirect(url_for("index"))
    if request.method == "POST":
        email = (request.form.get("email") or "").strip().lower()
        senha = (request.form.get("senha") or "").strip()
        user = buscar_por_email(email)
        if user and verificar_senha(user, senha):
            if not user.ativo:
                flash("Conta inativa. Entre em contato com o administrador.", "error")
                return render_template("login.html")
            login_user(user, remember=True)
            registrar_acesso(int(user.id))
            return redirect(url_for("index"))
        flash("E-mail ou senha incorretos.", "error")
    return render_template("login.html")


@app.route("/logout")
@login_required
def logout():
    logout_user()
    return redirect(url_for("login"))


@app.route("/admin")
@login_required
def admin():
    if not current_user.is_super_admin:
        return redirect(url_for("index"))
    usuarios = listar_usuarios()
    contas   = listar_contas()
    # Monta mapa conta_id -> ghl_config para exibir status no painel
    ghl_status = {}
    for c in contas:
        cfg = get_ghl_config(c["id"])
        ghl_status[c["id"]] = cfg["location_name"] if cfg else None
    return render_template("admin.html", usuarios=usuarios, contas=contas, ghl_status=ghl_status)


@app.route("/admin/criar-usuario", methods=["POST"])
@login_required
def admin_criar_usuario():
    if not current_user.is_super_admin:
        return redirect(url_for("index"))
    nome     = (request.form.get("nome") or "").strip()
    email    = (request.form.get("email") or "").strip()
    senha    = (request.form.get("senha") or "").strip()
    plano    = (request.form.get("plano") or "beta").strip()
    conta_id = request.form.get("conta_id") or None
    if conta_id:
        conta_id = int(conta_id)
    user, erro = criar_usuario(nome, email, senha, plano, conta_id)
    if erro:
        flash(f"Erro ao criar usuário: {erro}", "error")
    else:
        flash(f"Usuário {nome} criado.", "success")
    return redirect(url_for("admin"))


@app.route("/admin/usuario/<int:uid>/conta", methods=["POST"])
@login_required
def admin_atribuir_conta(uid):
    if not current_user.is_super_admin:
        return redirect(url_for("index"))
    conta_id = request.form.get("conta_id") or None
    plano    = request.form.get("plano") or None
    updates  = {}
    if conta_id is not None:
        updates["conta_id"] = int(conta_id) if conta_id else None
    if plano:
        updates["plano"] = plano
    if updates:
        atualizar_usuario(uid, **updates)
    return redirect(url_for("admin"))


@app.route("/admin/toggle-usuario/<int:uid>", methods=["POST"])
@login_required
def admin_toggle_usuario(uid):
    if not current_user.is_super_admin:
        return redirect(url_for("index"))
    user = User.get(uid)
    if user:
        novo_status = 0 if user.ativo else 1
        atualizar_usuario(uid, ativo=novo_status)
    return redirect(url_for("admin"))


@app.route("/admin/deletar-usuario/<int:uid>", methods=["POST"])
@login_required
def admin_deletar_usuario(uid):
    if not current_user.is_super_admin:
        return redirect(url_for("index"))
    if str(uid) != current_user.id:
        deletar_usuario(uid)
    return redirect(url_for("admin"))


@app.route("/")
@login_required
def index():
    return render_template("index.html",
                           is_admin=current_user.is_super_admin,
                           is_conta_admin=current_user.is_conta_admin)


@app.route("/api/conectar", methods=["POST"])
@login_required
def api_conectar():
    """Valida credenciais do CRM e retorna configuração."""
    data = request.get_json()
    crm_type = (data.get("crm_type") or "ghl").strip().lower()
    token = (data.get("token") or "").strip()
    # Remove prefixo "Bearer " se o usuário colou com ele
    if token.lower().startswith("bearer "):
        token = token[7:].strip()

    if not token:
        return jsonify({"ok": False, "erro": "Token obrigatório"}), 400

    # ── UNNICHAT ──
    if crm_type == "unnichat":
        connection_id = (data.get("connection_id") or "").strip()
        try:
            headers = {"Authorization": f"Bearer {token}"}
            if connection_id:
                headers["x-connection-id"] = connection_id
            r = requests.get(f"{UNNICHAT_BASE}/api/tags", headers=headers, timeout=10)
            if r.status_code == 200:
                return jsonify({"ok": True, "crm_type": "unnichat", "location_name": "Unnichat", "connection_id": connection_id})
            return jsonify({"ok": False, "erro": f"Token inválido (HTTP {r.status_code})"}), 200
        except requests.exceptions.Timeout:
            return jsonify({"ok": False, "erro": "Timeout ao conectar com o Unnichat"}), 200
        except Exception as e:
            return jsonify({"ok": False, "erro": str(e)}), 200

    # ── CRM (padrão) ──
    location_id = (data.get("location_id") or "").strip()
    if not location_id:
        return jsonify({"ok": False, "erro": "token e location_id sao obrigatorios"}), 400

    try:
        r = requests.get(
            f"{GHL_BASE}/opportunities/pipelines",
            headers=_ghl_headers(token),
            params={"locationId": location_id},
            timeout=10
        )
        if r.status_code != 200:
            return jsonify({"ok": False, "erro": f"Credenciais invalidas (HTTP {r.status_code})"}), 200

        pipelines_raw = r.json().get("pipelines", [])
        location_name = location_id
        try:
            r_loc = requests.get(
                f"{GHL_BASE}/locations/{location_id}",
                headers=_ghl_headers(token),
                timeout=8
            )
            if r_loc.status_code == 200:
                loc_data = r_loc.json()
                location_name = (
                    loc_data.get("location", {}).get("name")
                    or loc_data.get("name")
                    or location_id
                )
        except Exception:
            pass

        pipelines = []
        for p in pipelines_raw:
            stages = [{"id": s.get("id"), "name": s.get("name")} for s in p.get("stages", [])]
            pipelines.append({"id": p.get("id"), "name": p.get("name"), "stages": stages})

        return jsonify({"ok": True, "crm_type": "ghl", "location_name": location_name, "pipelines": pipelines})

    except requests.exceptions.Timeout:
        return jsonify({"ok": False, "erro": "Timeout ao conectar com o CRM"}), 200
    except Exception as e:
        return jsonify({"ok": False, "erro": str(e)}), 200


@app.route("/api/ghl-config", methods=["GET"])
@login_required
def api_get_ghl_config():
    """Retorna config GHL da conta do usuário para auto-conectar."""
    conta_id = current_user.conta_id
    # super_admin sem conta: aceita ?conta_id= na query
    if current_user.is_super_admin and not conta_id:
        conta_id = request.args.get("conta_id", type=int)
    if not conta_id:
        return jsonify({"ok": False, "configurado": False})
    cfg = get_ghl_config(conta_id)
    if not cfg or not cfg.get("token"):
        return jsonify({"ok": False, "configurado": False, "conta_id": conta_id})
    return jsonify({
        "ok": True, "configurado": True,
        "crm_type": cfg["crm_type"], "token": cfg["token"],
        "location_id": cfg["location_id"], "pipeline_id": cfg["pipeline_id"],
        "stage_id": cfg["stage_id"], "location_name": cfg["location_name"],
        "pipeline_name": cfg["pipeline_name"], "stage_name": cfg["stage_name"],
    })


@app.route("/api/ghl-config", methods=["POST"])
@login_required
def api_save_ghl_config():
    """Salva config GHL da conta (conta_admin ou super_admin)."""
    if not current_user.is_conta_admin:
        return jsonify({"ok": False, "erro": "Permissão negada."}), 403
    data = request.get_json()
    token = (data.get("token") or "").strip()
    if not token:
        return jsonify({"ok": False, "erro": "Token obrigatório."}), 400
    # super_admin pode salvar para qualquer conta via campo conta_id
    conta_id = current_user.conta_id
    if current_user.is_super_admin:
        conta_id = data.get("conta_id") or conta_id
    if not conta_id:
        return jsonify({"ok": False, "erro": "Conta não definida."}), 400
    save_ghl_config(
        conta_id=int(conta_id),
        crm_type=data.get("crm_type", "ghl"),
        token=token,
        location_id=data.get("location_id", ""),
        pipeline_id=data.get("pipeline_id", ""),
        stage_id=data.get("stage_id", ""),
        location_name=data.get("location_name", ""),
        pipeline_name=data.get("pipeline_name", ""),
        stage_name=data.get("stage_name", ""),
    )
    return jsonify({"ok": True})


# ── QUOTA DE LEADS ────────────────────────────────────────

@app.route("/api/quota", methods=["GET"])
@login_required
def api_quota():
    """Retorna a quota mensal de leads da conta do usuário logado."""
    conta_id = current_user.conta_id
    if not conta_id:
        # Sem conta vinculada: retorna limite zerado (não pode prospectar)
        return jsonify({
            "ok": True, "limite": 0, "usados": 0,
            "disponivel": 0, "reset_em": None,
            "sem_conta": True
        })
    try:
        resetar_quota_se_novo_mes(conta_id)
        q = get_quota(conta_id)
        return jsonify({
            "ok": True,
            "limite": q["limite"],
            "usados": q["usados"],
            "disponivel": q["disponivel"],
            "reset_em": q["reset_em"],
        })
    except Exception as e:
        return jsonify({"ok": False, "erro": str(e)}), 500


# ── ROTAS DE CONTAS (super_admin) ─────────────────────────

@app.route("/admin/conta/criar", methods=["POST"])
@login_required
def admin_criar_conta():
    if not current_user.is_super_admin:
        return jsonify({"ok": False, "erro": "Permissão negada."}), 403
    nome = (request.form.get("nome") or "").strip()
    if not nome:
        flash("Nome da conta é obrigatório.", "error")
        return redirect(url_for("admin"))
    criar_conta(nome)
    flash(f"Conta '{nome}' criada.", "success")
    return redirect(url_for("admin"))


@app.route("/admin/conta/<int:cid>/deletar", methods=["POST"])
@login_required
def admin_deletar_conta(cid):
    if not current_user.is_super_admin:
        return jsonify({"ok": False, "erro": "Permissão negada."}), 403
    deletar_conta(cid)
    flash("Conta removida.", "success")
    return redirect(url_for("admin"))


@app.route("/admin/conta/<int:cid>/renomear", methods=["POST"])
@login_required
def admin_renomear_conta(cid):
    if not current_user.is_super_admin:
        return jsonify({"ok": False, "erro": "Permissão negada."}), 403
    nome = (request.form.get("nome") or "").strip()
    if nome:
        renomear_conta(cid, nome)
    return redirect(url_for("admin"))


@app.route("/prospectar", methods=["POST"])
@login_required
def prospectar():
    if not current_user.ativo:
        return jsonify({"erro": "Conta inativa. Entre em contato com o administrador."}), 403
    data = request.get_json()
    nicho = (data.get("nicho") or "").strip()
    cidade = (data.get("cidade") or "").strip()
    limite = int(data.get("limite") or 20)
    crm_type = (data.get("crm_type") or "ghl").strip().lower()
    token = (data.get("token") or "").strip()

    if not nicho or not cidade:
        return jsonify({"erro": "Nicho e cidade sao obrigatorios"}), 400
    if not token:
        return jsonify({"erro": "Credenciais do CRM incompletas."}), 400

    # Campos CRM
    location_id = (data.get("location_id") or "").strip()
    pipeline_id = (data.get("pipeline_id") or "").strip()
    stage_id = (data.get("stage_id") or "").strip()
    connection_id = (data.get("connection_id") or "").strip()

    if crm_type == "ghl" and not (location_id and pipeline_id and stage_id):
        return jsonify({"erro": "Credenciais do CRM incompletas. Configure o CRM antes de prospectar."}), 400

    pais = (data.get("pais") or "BR").strip().upper()
    enriquecimento_auto = bool(data.get("enriquecimento_auto", False))

    limite = max(5, min(200, limite))

    # ── Verificação de quota mensal da conta ──
    conta_id = current_user.conta_id
    if conta_id:
        try:
            resetar_quota_se_novo_mes(conta_id)
            quota = get_quota(conta_id)
            if quota["usados"] >= quota["limite"]:
                return jsonify({
                    "ok": False,
                    "erro": f"Quota mensal atingida. Limite: {quota['limite']} leads/mês."
                }), 200
        except Exception:
            pass

    job_id = _novo_job(nicho, cidade, limite)

    t = threading.Thread(
        target=run_prospector,
        args=(job_id, nicho, cidade, limite, token, location_id, pipeline_id, stage_id,
              crm_type, connection_id, pais, enriquecimento_auto, conta_id),
        daemon=True
    )
    t.start()

    return jsonify({"job_id": job_id, "mensagem": f"Job {job_id} iniciado"})


@app.route("/status/<job_id>")
@login_required
def status(job_id):
    job = jobs.get(job_id)
    if not job:
        return jsonify({"erro": "Job nao encontrado"}), 404

    pct = 0
    if job["total"] > 0:
        pct = round((job["progresso"] / job["total"]) * 100)
    if job["status"] == "concluido":
        pct = 100

    return jsonify({
        "id": job["id"],
        "status": job["status"],
        "progresso": job["progresso"],
        "total": job["total"],
        "pct": pct,
        "leads_enviados": job["leads_enviados"],
        "logs": job["logs"][-10:],
        "erro": job["erro"],
        "concluido_em": job["concluido_em"],
        "leads": job["leads"],
    })


@app.route("/jobs")
@login_required
def listar_jobs():
    lista = []
    for job in sorted(jobs.values(), key=lambda j: j["criado_em"], reverse=True)[:10]:
        tipo = job.get("tipo", "prospeccao")
        lista.append({
            "id": job["id"],
            "tipo": tipo,
            "nicho": job.get("nicho", "—"),
            "cidade": job.get("cidade", "—"),
            "limite": job.get("limite", job.get("total", 0)),
            "status": job["status"],
            "progresso": job["progresso"],
            "total": job["total"],
            "leads_enviados": job["leads_enviados"],
            "criado_em": job["criado_em"],
            "concluido_em": job["concluido_em"],
        })
    return jsonify(lista)


@app.route("/cancelar/<job_id>", methods=["POST"])
@login_required
def cancelar(job_id):
    job = jobs.get(job_id)
    if not job:
        return jsonify({"erro": "Job nao encontrado"}), 404
    if job["status"] not in ("aguardando", "em_andamento"):
        return jsonify({"erro": f"Job ja esta em status '{job['status']}'"}), 400
    job["status"] = "cancelado"
    return jsonify({"mensagem": f"Job {job_id} marcado para cancelamento"})


@app.route("/pausar/<job_id>", methods=["POST"])
@login_required
def pausar(job_id):
    job = jobs.get(job_id)
    if not job:
        return jsonify({"erro": "Job nao encontrado"}), 404
    if job["status"] not in ("aguardando", "em_andamento"):
        return jsonify({"erro": f"Job ja esta em status '{job['status']}'"}), 400
    job["status"] = "pausado"
    return jsonify({"mensagem": f"Job {job_id} pausado - enviando leads coletados ao CRM"})


@app.route("/enriquecer", methods=["POST"])
@login_required
def enriquecer():
    """Recebe CSV com nomes de empresas ou lista JSON, enriquece e envia ao CRM."""
    token = ""
    location_id = ""
    pipeline_id = ""
    stage_id = ""
    empresas = []

    if request.is_json:
        data = request.get_json()
        token = (data.get("token") or "").strip()
        location_id = (data.get("location_id") or "").strip()
        pipeline_id = (data.get("pipeline_id") or "").strip()
        stage_id = (data.get("stage_id") or "").strip()
        empresas = data.get("empresas", [])
    else:
        token = (request.form.get("token") or "").strip()
        location_id = (request.form.get("location_id") or "").strip()
        pipeline_id = (request.form.get("pipeline_id") or "").strip()
        stage_id = (request.form.get("stage_id") or "").strip()

        # Aceita CSV upload
        if "arquivo" in request.files:
            f = request.files["arquivo"]
            content = f.read().decode("utf-8", errors="ignore")
            reader = csv.reader(io.StringIO(content))
            for row in reader:
                if row and row[0].strip() and row[0].strip().lower() != "empresa":
                    empresas.append(row[0].strip())

        # Aceita texto colado (um nome por linha)
        lista_texto = (request.form.get("lista_texto") or "").strip()
        if lista_texto and not empresas:
            for linha in lista_texto.splitlines():
                nome = linha.strip()
                if nome:
                    empresas.append(nome)

    if not empresas:
        return jsonify({"erro": "Nenhuma empresa na lista"}), 400
    if not token or not location_id:
        return jsonify({"erro": "Token e Location ID obrigatórios"}), 400

    empresas = empresas[:100]  # limite de 100 por vez
    job_id = _novo_job_enriq(empresas)

    t = threading.Thread(
        target=run_enriquecedor,
        args=(job_id, empresas, token, location_id, pipeline_id, stage_id),
        daemon=True
    )
    t.start()

    return jsonify({"job_id": job_id, "mensagem": f"Enriquecendo {len(empresas)} empresas"})


# Arquivo local para guardar IDs dos campos personalizados criados no CRM
CAMPOS_PATH = Path("/opt/mia/workspace/prospeccao_ativa/ghl_campos.json")

CAMPOS_ENRIQUECIMENTO_BR = [
    {"name": "CNPJ",            "dataType": "TEXT", "placeholder": "00.000.000/0001-00"},
    {"name": "LinkedIn",        "dataType": "TEXT", "placeholder": "https://linkedin.com/in/..."},
    {"name": "Decisor",         "dataType": "TEXT", "placeholder": "Nome do sócio/decisor"},
    {"name": "Cargo do Decisor","dataType": "TEXT", "placeholder": "Ex: Sócio-Administrador"},
]

CAMPOS_ENRIQUECIMENTO_US = [
    {"name": "Email",           "dataType": "TEXT", "placeholder": "email@empresa.com"},
    {"name": "LinkedIn",        "dataType": "TEXT", "placeholder": "https://linkedin.com/in/..."},
    {"name": "Decisor",         "dataType": "TEXT", "placeholder": "Owner / CEO / Founder"},
    {"name": "Cargo do Decisor","dataType": "TEXT", "placeholder": "Ex: Owner, CEO, Manager"},
]

# Mantém compatibilidade — padrão BR
CAMPOS_ENRIQUECIMENTO = CAMPOS_ENRIQUECIMENTO_BR


def _carregar_campos_ghl() -> dict:
    """Retorna mapeamento nome_campo -> field_id salvo localmente."""
    try:
        if CAMPOS_PATH.exists():
            return json.loads(CAMPOS_PATH.read_text())
    except Exception:
        pass
    return {}


def _salvar_campos_ghl(campos: dict):
    try:
        CAMPOS_PATH.write_text(json.dumps(campos))
    except Exception:
        pass


@app.route("/api/configurar-campos-ghl", methods=["POST"])
@login_required
def configurar_campos_ghl():
    """Cria campos personalizados no CRM e salva os IDs localmente."""
    data = request.get_json(force=True) or {}
    token = (data.get("token") or "").strip()
    location_id = (data.get("location_id") or "").strip()

    if not token or not location_id:
        return jsonify({"erro": "Token e Location ID obrigatórios"}), 400

    # Remove "Bearer " se vier com prefixo
    if token.lower().startswith("bearer "):
        token = token[7:].strip()

    pais = (data.get("pais") or "BR").strip().upper()
    campos_a_criar = CAMPOS_ENRIQUECIMENTO_US if pais == "US" else CAMPOS_ENRIQUECIMENTO_BR

    campos_salvos = _carregar_campos_ghl()
    criados = []
    ja_existiam = []
    erros = []

    # Busca campos existentes para não duplicar
    try:
        r_list = requests.get(
            f"{GHL_BASE}/locations/{location_id}/customFields",
            headers=_ghl_headers(token),
            timeout=10
        )
        existentes = {}
        if r_list.status_code == 200:
            for f in r_list.json().get("customFields", []):
                existentes[f.get("name", "").strip()] = f.get("id")
    except Exception:
        existentes = {}

    for campo in campos_a_criar:
        nome = campo["name"]
        # Já existe no CRM - salva o ID
        if nome in existentes:
            campos_salvos[nome] = existentes[nome]
            ja_existiam.append(nome)
            continue
        # Tenta criar - experimenta os dois parâmetros que o GHL pode aceitar
        criado = False
        for body_extra in [{"model": "opportunity"}, {"objectKey": "opportunity"}, {}]:
            try:
                body = {"name": nome, "dataType": campo["dataType"], "position": 0}
                body.update(body_extra)
                r = requests.post(
                    f"{GHL_BASE}/locations/{location_id}/customFields",
                    headers=_ghl_headers(token),
                    json=body,
                    timeout=10
                )
                if r.status_code in (200, 201):
                    resp = r.json()
                    field_id = (resp.get("customField") or {}).get("id") or resp.get("id")
                    obj_key = (resp.get("customField") or {}).get("objectKey") or resp.get("objectKey", "contact")
                    if field_id:
                        campos_salvos[nome] = field_id
                        tipo = "oportunidade" if "opportun" in str(obj_key).lower() else "contato"
                        criados.append(f"{nome} (tipo: {tipo})")
                        criado = True
                    break
            except Exception as e:
                erros.append(f"{nome}: {str(e)[:60]}")
                break
        if not criado and nome not in campos_salvos:
            try:
                msg = r.json().get("message", r.text[:80])
            except Exception:
                msg = "sem resposta"
            erros.append(f"{nome}: {msg}")

    _salvar_campos_ghl(campos_salvos)

    return jsonify({
        "criados": criados,
        "ja_existiam": ja_existiam,
        "erros": erros,
        "campos": campos_salvos,
    })


@app.route("/api/campos-ghl")
@login_required
def listar_campos_ghl():
    """Retorna os campos salvos localmente."""
    return jsonify(_carregar_campos_ghl())


if __name__ == "__main__":
    init_db()
    print("Iniciando Prospeccao Web na porta 5050...")
    app.run(host="0.0.0.0", port=5050, debug=False)
