<?php
// public/api/conexoes.php - VERSÃO COM MODAL DE CONEXÃO E POLLING EM TEMPO REAL

ini_set('display_errors', 1);
error_reporting(E_ALL);

require_once __DIR__ . '/../../app/config.php';
require_once __DIR__ . '/../../app/db.php';

header('Content-Type: application/json; charset=utf-8');

function jexit(array $payload, int $status = 200) {
  http_response_code($status);
  echo json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
  exit;
}

if (empty($_SESSION['user']['email'])) {
  jexit(['ok' => false, 'error' => 'não autenticado'], 401);
}
$EMAIL = $_SESSION['user']['email'];
$pdo   = db();

function base_url_for_user(?array $row): string {
  $fallback = rtrim((defined('EV_API_URL') ? EV_API_URL : ''), '/');
  $u = isset($row['url_api']) && trim((string)$row['url_api']) !== '' ? rtrim($row['url_api'], '/') : $fallback;
  return $u ?: $fallback;
}

function ev_pick_endpoint(PDO $pdo): array {
  $eps = ev_endpoints();
  if (!$eps) jexit(['ok'=>false,'error'=>'Nenhum endpoint UazAPI configurado'],500);

  $counts = [];
  foreach ($eps as $ep) $counts[$ep['url']] = 0;

  $sql = "select url_api, count(*) qty from users where url_api is not null and instance_name is not null group by url_api";
  try {
    foreach ($pdo->query($sql) as $r) {
      $u = rtrim((string)$r['url_api'], '/');
      if (isset($counts[$u])) $counts[$u] = (int)$r['qty'];
    }
  } catch (Throwable $e) {
    shuffle($eps);
    return $eps[0];
  }

  asort($counts);
  $best = array_key_first($counts);
  foreach ($eps as $ep) if ($ep['url'] === $best) return $ep;
  shuffle($eps);
  return $eps[0];
}

$request = function (array $opts) {
  $ch = curl_init();
  curl_setopt_array($ch, $opts + [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 10,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_MAXREDIRS      => 2,
  ]);
  $resp = curl_exec($ch);
  $err  = curl_error($ch);
  $code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
  curl_close($ch);
  return [$code, $resp, $err];
};

function try_headers(callable $request, string $url, array $headersList, array $extra = [], string $method = 'GET') {
  if (!empty($headersList) && isset($headersList[0]) && is_string($headersList[0])) {
    $headersList = [$headersList];
  }
  $last = [0, null, ''];
  foreach ($headersList as $hs) {
    if (!is_array($hs)) continue;
    $opts = [CURLOPT_URL => $url, CURLOPT_HTTPHEADER => $hs] + $extra;
    if ($method === 'POST') $opts[CURLOPT_POST] = true;
    elseif ($method !== 'GET') $opts[CURLOPT_CUSTOMREQUEST] = $method;
    $last = $request($opts);
    [$code, $resp] = $last;
    if ($code >= 200 && $code < 400 && $resp) return $last;
  }
  return $last;
}

function getUserRow(PDO $pdo, string $email) {
  $s = $pdo->prepare('select * from users where email = :e limit 1');
  $s->execute([':e'=>$email]);
  return $s->fetch() ?: null;
}

function createUserIfMissing(PDO $pdo, string $email, ?string $name=null) {
  $s = $pdo->prepare('insert into users (email, name) values (:e,:n) on conflict (email) do nothing');
  $s->execute([':e'=>$email, ':n'=>$name]);
}

function saveInstanceOnUser(PDO $pdo, string $email, string $name, ?string $token, array $meta, string $state='created', ?string $urlApi=null) {
  $pdo->exec('DEALLOCATE ALL');
  $s = $pdo->prepare('update users set instance_name = :n, api_key = :k, meta = :m, state = :st, url_api = :u where email = :e returning *');
  $result = $s->execute([':e'=>$email, ':n'=>$name, ':k'=>$token, ':m'=>json_encode($meta), ':st'=>$state, ':u'=>$urlApi]);
  if (!$result) throw new Exception('Failed to save instance on user');
  return $s->fetch();
}

function updateState(PDO $pdo, string $email, string $state, ?array $lastQR=null) {
  $s = $pdo->prepare('update users set state=:st, last_qr=:qr where email=:e');
  $s->execute([':st'=>$state, ':qr'=>$lastQR?json_encode($lastQR):null, ':e'=>$email]);
}

function setAgenteLigado(PDO $pdo, string $email, bool $on): void {
  $pdo->prepare('update users set agenteligado=:on where email=:e')->execute([':on'=>$on?1:0, ':e'=>$email]);
}

function getResponderTodos(PDO $pdo, string $email): bool {
  $r = getUserRow($pdo,$email);
  return (bool)($r['responder_todos'] ?? false);
}

function setResponderTodos(PDO $pdo, string $email, bool $on): void {
  $pdo->prepare('update users set responder_todos=:on where email=:e')->execute([':on'=>$on?1:0, ':e'=>$email]);
}

function arr_get_path(array $a, string $path) {
  $cur = $a;
  foreach (explode('.', $path) as $seg) {
    if (is_array($cur) && array_key_exists($seg, $cur)) $cur = $cur[$seg];
    else return null;
  }
  return $cur;
}

function is_connected(array $d): bool {
  $candidates = [];
  $prefixes   = ['', 'data.', 'instance.', 'result.'];
  $keys       = ['status','state','connectionStatus'];

  foreach ($prefixes as $pre) {
    foreach ($keys as $k) {
      $val = arr_get_path($d, $pre.$k);
      if ($val !== null) $candidates[] = $val;
    }
  }
  
  foreach ($candidates as $val) {
    if (is_string($val)) {
      $lower = strtolower($val);
      if ($lower === 'connected' || $lower === 'open') return true;
    }
    if (is_scalar($val)) {
      $lower = strtolower((string)$val);
      if ($lower === 'connected' || $lower === 'open') return true;
    }
    if (is_array($val)) {
      $maybe = $val['status'] ?? $val['state'] ?? $val['value'] ?? reset($val);
      if (is_string($maybe) || is_scalar($maybe)) {
        $lower = strtolower((string)$maybe);
        if ($lower === 'connected' || $lower === 'open') return true;
      }
    }
  }
  return false;
}

function deep_search_qr(array $data, array &$found = []): void {
  foreach ($data as $key => $value) {
    $keyLower = is_string($key) ? strtolower($key) : '';

    // ── Código de pareamento: deve ser curto (4-20 chars), alfanumérico.
    //    Chaves aceitas: paircode (UazAPI), pairingcode
    if (in_array($keyLower, ['paircode', 'pairingcode']) && is_string($value)) {
      $clean = preg_replace('/[^A-Za-z0-9\-]/', '', $value);
      // Código válido: entre 4 e 20 caracteres (ex: "7LD2-JYKL" ou "27458392")
      if (strlen($clean) >= 4 && strlen($clean) <= 20) {
        $found['pairingCode'] = $clean;
      }
    }

    // ── QR string (raw string do QR code — geralmente começa com "2@" ou "1@" no WhatsApp)
    if (in_array($keyLower, ['qr', 'qrcode', 'qr_string', 'qrstring']) && is_string($value) && strlen($value) > 10) {
      // Evita capturar base64 puro de imagem (que conteria muitos '=' e '/')
      if (!preg_match('/^[A-Za-z0-9+\/]{100,}={0,2}$/', $value)) {
        $found['qrString'] = $value;
      }
    }

    // ── Imagem base64: chaves específicas de imagem, string longa
    if (in_array($keyLower, ['base64', 'image', 'qr_base64', 'qrimage', 'qr_image']) && is_string($value) && strlen($value) > 100) {
      $found['qrImage'] = $value;
    }

    if (is_array($value)) {
      deep_search_qr($value, $found);
    } elseif (is_object($value)) {
      deep_search_qr((array)$value, $found);
    }
  }
}

function extract_qr_payload(array $d): ?array {
  $found = [];
  deep_search_qr($d, $found);
  
  if (!empty($found['pairingCode'])) {
    return [
      'kind' => 'pairingCode',
      'code' => $found['pairingCode'],
      'pairingCode' => $found['pairingCode']
    ];
  }
  
  if (!empty($found['qrString'])) {
    return [
      'kind' => 'string',
      'code' => $found['qrString'],
      'qrString' => $found['qrString']
    ];
  }
  
  if (!empty($found['qrImage'])) {
    $img = $found['qrImage'];
    if (strpos($img, 'data:image') !== 0) {
      $img = 'data:image/png;base64,' . $img;
    }
    return [
      'kind' => 'image',
      'image' => $img,
      'img' => $img
    ];
  }
  
  return null;
}

function setWebhookMeta(PDO $pdo, string $email, bool $enabled, ?string $url=null) {
  $row = getUserRow($pdo,$email);
  $meta = [];
  if ($row && $row['meta']) {
    $meta = is_array($row['meta']) ? $row['meta'] : (json_decode($row['meta'], true) ?: []);
    if (!is_array($meta)) $meta = [];
  }
  $prevUrl = $meta['webhook']['url'] ?? null;
  $meta['webhook'] = [
    'enabled' => $enabled,
    'url'     => $url ?? $prevUrl ?? (defined('EV_WEBHOOK_URL') ? EV_WEBHOOK_URL : null),
    'events'  => ['messages.upsert' => $enabled],
  ];
  $pdo->prepare('update users set meta=:m where email=:e')->execute([':m'=>json_encode($meta), ':e'=>$email]);
  return $meta['webhook'];
}

function getWebhookMeta(PDO $pdo, string $email): array {
  $row = getUserRow($pdo,$email);
  $meta = [];
  if ($row && $row['meta']) {
    $meta = is_array($row['meta']) ? $row['meta'] : (json_decode($row['meta'], true) ?: []);
    if (!is_array($meta)) $meta = [];
  }
  $wb = $meta['webhook'] ?? null;
  if (!is_array($wb)) {
    return ['enabled'=>false,'url'=>(defined('EV_WEBHOOK_URL') ? EV_WEBHOOK_URL : null),'events'=>['messages.upsert'=>false]];
  }
  $wb += [
    'enabled'=>(bool)($wb['enabled'] ?? false),
    'url'    =>$wb['url'] ?? (defined('EV_WEBHOOK_URL') ? EV_WEBHOOK_URL : null),
    'events' =>is_array($wb['events'] ?? null) ? $wb['events'] : ['messages.upsert'=>(bool)($wb['enabled'] ?? false)],
  ];
  return $wb;
}

function admin_headers(string $adminToken): array {
  return ['Content-Type: application/json','Accept: application/json','admintoken: '.$adminToken];
}

function instance_headers(string $instanceToken): array {
  return ['Content-Type: application/json','Accept: application/json','token: '.$instanceToken];
}

$action = $_GET['action'] ?? '';

try {
  switch ($action) {

    case 'me': {
      createUserIfMissing($pdo, $EMAIL, $_SESSION['user']['name'] ?? null);
      $row = getUserRow($pdo, $EMAIL);
      $rt  = getResponderTodos($pdo, $EMAIL);
      jexit(['ok'=>true,'user'=>$row,'webhook'=>getWebhookMeta($pdo,$EMAIL),'responder_todos'=>$rt]);
    }

    case 'status': {
      $row = getUserRow($pdo,$EMAIL);
      if (!$row || empty($row['instance_name'])) {
        jexit(['ok'=>true,'connected'=>false,'webhook'=>getWebhookMeta($pdo,$EMAIL)]);
      }

      $baseUrl = base_url_for_user($row);
      $token   = (string)($row['api_key'] ?? '');
      $hdrs    = instance_headers($token);

      [$code,$resp] = try_headers($request, rtrim($baseUrl,'/').'/instance/status', $hdrs, [CURLOPT_HTTPGET=>true], 'GET');

      $connected = false;
      
      if ($resp && $code >= 200 && $code < 400) {
        $d = json_decode($resp,true) ?: [];
        $connected = is_connected($d);
        updateState($pdo, $EMAIL, $connected ? 'open' : 'disconnected');
      } else {
        if ($row && isset($row['state'])) {
          $connected = ($row['state'] === 'open');
        }
      }

      jexit(['ok'=>true,'connected'=>$connected,'webhook'=>getWebhookMeta($pdo,$EMAIL)]);
    }

    case 'delete': {
      $row = getUserRow($pdo,$EMAIL);
      if ($row && !empty($row['instance_name'])) {
        $baseUrl = base_url_for_user($row);
        $token   = (string)$row['api_key'];
        $request([
          CURLOPT_URL           => $baseUrl.'/instance',
          CURLOPT_CUSTOMREQUEST => 'DELETE',
          CURLOPT_HTTPHEADER    => instance_headers($token),
          CURLOPT_TIMEOUT       => 10,
        ]);
      }

      $pdo->prepare("UPDATE users SET instance_name=null, api_key=null, meta='{}'::jsonb, state='none', last_qr=null, agenteligado=false, url_api=null WHERE email=:e")->execute([':e'=>$EMAIL]);
      jexit(['ok'=>true,'status'=>200,'deleted'=>true]);
    }

    case 'create': {
      createUserIfMissing($pdo, $EMAIL, $_SESSION['user']['name'] ?? null);
      $row = getUserRow($pdo,$EMAIL);
      if (!empty($row['instance_name'])) {
        jexit(['ok'=>false,'error'=>'Você já possui uma instância.','instance'=>$row['instance_name']],409);
      }

      $instance = trim($_POST['instance'] ?? '');
      if ($instance === '') $instance = 'evolua-'.substr(bin2hex(random_bytes(3)),0,6);

      $endpoint = ev_pick_endpoint($pdo);
      $baseUrl  = rtrim($endpoint['url'],'/');
      $adminKey = $endpoint['key'];

      $body = [
        "name"         => $instance,
        "systemName"   => $instance,
        "adminField01" => "user=".$EMAIL,
        "adminField02" => "origin=evolua",
      ];

      [$code,$resp] = try_headers(
        $request,
        $baseUrl.'/instance/init',
        [['Content-Type: application/json','Accept: application/json','adminToken: '.$adminKey]],
        [CURLOPT_POSTFIELDS => json_encode($body, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES)],
        'POST'
      );

      if ($code >= 400 || !$resp) {
        jexit(['ok'=>false,'status'=>$code,'error'=>'Falha ao criar (init)','raw'=>$resp], $code ?: 500);
      }

      $j = json_decode($resp,true) ?: [];
      $instToken = $j['token'] ?? null;
      $instName  = $j['name'] ?? $instance;
      if (!$instToken) jexit(['ok'=>false,'error'=>'Init não retornou token da instância','raw'=>$j],500);

      $saved = saveInstanceOnUser($pdo,$EMAIL,$instName,$instToken,$j,'disconnected',$baseUrl);

      $adminUpdateBody = ["instanceName"=>$instName,"admin"=>true];
      try_headers(
        $request,
        $baseUrl.'/instance/updateAdminFields',
        [['Content-Type: application/json','Accept: application/json','adminToken: '.$adminKey]],
        [CURLOPT_POSTFIELDS => json_encode($adminUpdateBody)],
        'POST'
      );

      $enabled = defined('EV_WEBHOOK_DEFAULT_ENABLED') ? (bool)EV_WEBHOOK_DEFAULT_ENABLED : true;
      $urlWb   = defined('EV_WEBHOOK_URL') ? EV_WEBHOOK_URL : null;
      if ($urlWb) {
        setWebhookMeta($pdo,$EMAIL,$enabled,$urlWb);
        setAgenteLigado($pdo,$EMAIL,$enabled);
      }

      jexit(['ok'=>true,'status'=>201,'instance'=>$saved['instance_name']]);
    }

    // ─── CONNECT: suporta QR Code (sem phone) e Código de Pareamento (com phone) ───
    case 'connect': {
      $row = getUserRow($pdo,$EMAIL);
      if (!$row || empty($row['instance_name'])) {
        jexit(['ok'=>false,'error'=>'Nenhuma instância cadastrada.'],404);
      }

      $baseUrl = base_url_for_user($row);
      $token   = (string)$row['api_key'];

      // Se vier phone, usa pareamento; caso contrário, gera QR
      $rawPhone = $_POST['phone'] ?? null;
      $phone    = is_string($rawPhone) ? preg_replace('/\D+/', '', trim($rawPhone)) : '';
      
      $bodyArr  = $phone !== '' ? ['phone' => $phone] : new stdClass();
      $body     = json_encode($bodyArr, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

      [$code, $resp, $err] = $request([
        CURLOPT_URL        => $baseUrl . '/instance/connect',
        CURLOPT_POST       => true,
        CURLOPT_HTTPHEADER => instance_headers($token),
        CURLOPT_POSTFIELDS => $body,
        CURLOPT_TIMEOUT    => 15,
      ]);

      if ($code >= 400 || !$resp) {
        jexit(['ok'=>false,'error'=>'Falha ao conectar','status'=>$code,'curl_error'=>$err,'raw'=>$resp], $code ?: 500);
      }

      $d = json_decode($resp, true) ?: [];

      // Verifica conexão imediata
      if (is_connected($d)) {
        updateState($pdo, $EMAIL, 'open');
        jexit(['ok'=>true,'connected'=>true]);
      }

      // Extrai QR ou código de pareamento
      if ($qr = extract_qr_payload($d)) {
        $mode = ($phone !== '') ? 'pairing' : 'qr';
        updateState($pdo, $EMAIL, 'disconnected', $qr);
        jexit(['ok'=>true,'qr'=>$qr,'mode'=>$mode,'raw'=>$d]);
      }

      updateState($pdo, $EMAIL, 'disconnected');
      jexit(['ok'=>true,'pending'=>true,'note'=>'Aguardando código/QR.','raw'=>$d]);
    }

    // ─── QRPOLL: polling rápido de status + novo QR se disponível ───
    case 'qrpoll': {
      $row = getUserRow($pdo, $EMAIL);
      if (!$row || empty($row['instance_name'])) {
        jexit(['ok'=>false,'error'=>'Nenhuma instância cadastrada.'], 404);
      }

      $baseUrl = base_url_for_user($row);
      $hdrs    = instance_headers((string)$row['api_key']);

      [$code, $resp, $err] = try_headers($request, $baseUrl.'/instance/status', $hdrs, [CURLOPT_HTTPGET=>true], 'GET');

      if ($code >= 400 || !$resp) {
        jexit(['ok'=>false,'error'=>'Falha ao buscar status','status'=>$code,'curl_error'=>$err], $code ?: 500);
      }

      $d = json_decode($resp, true) ?: [];

      // Conectado!
      if (is_connected($d)) {
        updateState($pdo, $EMAIL, 'open');
        jexit(['ok'=>true,'connected'=>true]);
      }

      // Novo QR disponível (pode ter atualizado)
      if ($qr = extract_qr_payload($d)) {
        updateState($pdo, $EMAIL, 'disconnected', $qr);
        jexit(['ok'=>true,'qr'=>$qr,'raw'=>$d]);
      }

      jexit(['ok'=>true,'pending'=>true,'note'=>'Aguardando conexão','raw'=>$d]);
    }

    case 'webhook_status': {
      $row = getUserRow($pdo,$EMAIL);
      if (!$row || empty($row['instance_name'])) jexit(['ok'=>false,'error'=>'Sem instância'],404);
      jexit(['ok'=>true,'meta'=>getWebhookMeta($pdo,$EMAIL),'api_available'=>false,'agenteligado'=>(bool)($row['agenteligado'] ?? false)]);
    }

    case 'webhook_toggle': {
      $row = getUserRow($pdo,$EMAIL);
      if (!$row || empty($row['instance_name'])) jexit(['ok'=>false,'error'=>'Sem instância'],404);

      $enabled = isset($_POST['enabled']) ? (bool)$_POST['enabled'] : null;
      if ($enabled === null) jexit(['ok'=>false,'error'=>'Parâmetro enabled obrigatório (1/0)'],400);

      $meta = getWebhookMeta($pdo,$EMAIL);
      $url  = $meta['url'] ?? (defined('EV_WEBHOOK_URL') ? EV_WEBHOOK_URL : null);
      if (!$url) jexit(['ok'=>false,'error'=>'EV_WEBHOOK_URL não definida'],400);

      setWebhookMeta($pdo,$EMAIL,$enabled,$url);
      setAgenteLigado($pdo,$EMAIL,$enabled);
      jexit(['ok'=>true,'meta'=>getWebhookMeta($pdo,$EMAIL),'mode'=>'local','agenteligado'=>$enabled]);
    }

    case 'responder_todos_status': {
      jexit(['ok'=>true,'responder_todos'=>getResponderTodos($pdo,$EMAIL)]);
    }

    case 'responder_todos_toggle': {
      $enabled = isset($_POST['enabled']) ? (bool)$_POST['enabled'] : null;
      if ($enabled === null) jexit(['ok'=>false,'error'=>'Parâmetro enabled obrigatório (1/0)'],400);
      setResponderTodos($pdo,$EMAIL,$enabled);
      jexit(['ok'=>true,'responder_todos'=>$enabled]);
    }

    default:
      jexit(['ok'=>false,'error'=>'Ação inválida'],400);
  }
} catch (Throwable $e) {
  jexit(['ok'=>false,'error'=>'Exceção: '.$e->getMessage(),'trace'=>$e->getTraceAsString()],500);
}