<?php
declare(strict_types=1);

/**
 * Cliente HTTP do CRM parceiro (Affiliate CR).
 *
 * Dois endpoints, ambos autenticados pelo header `x-api-key`:
 *
 *   POST /lead/register        cadastra o lead
 *   GET  /lead/external-leads  devolve os leads do período com status e FTD
 *
 * Todas as funções devolvem um array normalizado
 * ['ok' => bool, 'http' => int, 'error' => ?string, 'body' => array|null, 'raw' => string]
 * — nunca lançam exceção, porque uma falha de rede não pode derrubar o envio
 * do formulário na landing.
 */

require_once __DIR__ . '/config.php';

/**
 * Executa a chamada HTTP e decodifica a resposta JSON.
 *
 * @param array<string,mixed>|null $json  corpo JSON (POST) ou null (GET)
 * @param array<string,string>     $query querystring (GET)
 * @return array{ok:bool,http:int,error:?string,body:?array,raw:string}
 */
function partner_request(string $method, string $path, ?array $json = null, array $query = []): array
{
    $url = rtrim(PARTNER_API_BASE, '/') . '/' . ltrim($path, '/');
    if ($query !== []) {
        $url .= '?' . http_build_query($query);
    }

    $headers = [
        'x-api-key: ' . PARTNER_API_KEY,
        'Accept: application/json',
    ];
    $payload = null;
    if ($json !== null) {
        $payload = json_encode($json, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
        $headers[] = 'Content-Type: application/json';
    }

    $ch = curl_init($url);
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_CUSTOMREQUEST => $method,
        CURLOPT_FOLLOWLOCATION => true,
        CURLOPT_MAXREDIRS => 3,
        CURLOPT_TIMEOUT => PARTNER_TIMEOUT,
        CURLOPT_CONNECTTIMEOUT => PARTNER_CONNECT_TIMEOUT,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2,
        CURLOPT_USERAGENT => 'ZMarketsLanding/2.0',
        CURLOPT_HTTPHEADER => $headers,
    ]);
    if ($payload !== null) {
        curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
    }

    $raw = curl_exec($ch);
    $http = (int) curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
    $curlError = curl_errno($ch) !== 0 ? curl_error($ch) : null;
    curl_close($ch);

    if ($raw === false || $curlError !== null) {
        return ['ok' => false, 'http' => $http, 'error' => 'conexão: ' . ($curlError ?? 'falha desconhecida'), 'body' => null, 'raw' => ''];
    }

    $raw = (string) $raw;
    $body = json_decode($raw, true);
    if (!is_array($body)) {
        $snippet = trim(strip_tags($raw));
        return [
            'ok' => false,
            'http' => $http,
            'error' => 'resposta não-JSON (HTTP ' . $http . '): ' . mb_substr($snippet, 0, 180),
            'body' => null,
            'raw' => $raw,
        ];
    }

    // A API sinaliza sucesso tanto pelo HTTP quanto pelo campo `status`.
    $ok = $http >= 200 && $http < 300 && (($body['status'] ?? false) !== false);
    $error = $ok ? null : (string) ($body['message'] ?? ('HTTP ' . $http));

    return ['ok' => $ok, 'http' => $http, 'error' => $error, 'body' => $body, 'raw' => $raw];
}

/**
 * POST /lead/register
 *
 * @param array{first_name:string,last_name:string,phone:string,email:string,source:string,ip:string,country:string,funnel_name:string} $lead
 * @return array{ok:bool,http:int,error:?string,body:?array,raw:string}
 */
function partner_register_lead(array $lead): array
{
    return partner_request('POST', '/lead/register', $lead);
}

/**
 * GET /lead/external-leads?from=YYYY-MM-DD&to=YYYY-MM-DD[&ftd=1]
 *
 * @param bool $onlyFtd true = apenas leads que depositaram
 * @return array{ok:bool,http:int,error:?string,body:?array,raw:string}
 */
function partner_external_leads(string $from, string $to, bool $onlyFtd = false): array
{
    $query = ['from' => $from, 'to' => $to];
    if ($onlyFtd) {
        $query['ftd'] = '1';
    }
    return partner_request('GET', '/lead/external-leads', null, $query);
}

/**
 * O CRM já tinha esse telefone/e-mail? Isso não é falha: o lead está lá.
 * Confirmado em teste — responde HTTP 409 com
 * "The lead with same phone: 351912345678 already exists".
 *
 * @param array{ok:bool,http:int,error:?string,body:?array} $result
 */
function partner_is_duplicate(array $result): bool
{
    if ($result['ok']) {
        return false;
    }
    if (($result['http'] ?? 0) === 409) {
        return true;
    }
    $msg = mb_strtolower((string) ($result['error'] ?? ''));
    return str_contains($msg, 'already exist') || str_contains($msg, 'duplicat');
}

/**
 * O lead foi recusado porque o país não está habilitado na conta de afiliado?
 * ("Bad country" — vem com HTTP 200 e status:false.)
 *
 * @param array{ok:bool,http:int,error:?string,body:?array} $result
 */
function partner_is_bad_country(array $result): bool
{
    return !$result['ok'] && str_contains(mb_strtolower((string) ($result['error'] ?? '')), 'country');
}

/* ------------------------------------------------------------------ *
 * Helpers de normalização dos dados do formulário
 * ------------------------------------------------------------------ */

/**
 * Quebra "Maria Silva Souza" em ['Maria', 'Silva Souza'].
 * A API exige last_name; quando o visitante digita só um nome, repetimos o
 * primeiro em vez de mandar string vazia (que o CRM rejeita).
 *
 * @return array{0:string,1:string}
 */
function split_full_name(string $full): array
{
    $parts = preg_split('/\s+/u', trim($full), -1, PREG_SPLIT_NO_EMPTY) ?: [];
    if ($parts === []) {
        return ['', ''];
    }
    $first = array_shift($parts);
    $last = $parts === [] ? $first : implode(' ', $parts);
    return [$first, $last];
}

/** Telefone em E.164 sem o "+" — formato aceito pelo endpoint /lead/register. */
function normalize_phone(string $phone): string
{
    return ltrim(preg_replace('/[^\d]/', '', $phone) ?? '', '0');
}

/**
 * Descobre o país (ISO-3166 alpha-2) do lead.
 *
 * Ordem: o que o seletor de telefone mandou (`pais_iso`) → nome do país
 * digitado/rotulado (`pais`) → DDI do telefone. Retorna '' se nada bater,
 * já que o campo é opcional na API.
 */
function resolve_country_iso(string $isoFromForm, string $labelFromForm, string $phone): string
{
    $iso = strtoupper(trim($isoFromForm));
    if (preg_match('/^[A-Z]{2}$/', $iso)) {
        return $iso;
    }

    $label = mb_strtolower(trim($labelFromForm));
    if ($label !== '' && isset(COUNTRY_LABEL_TO_ISO[$label])) {
        return COUNTRY_LABEL_TO_ISO[$label];
    }

    return country_from_dial_code($phone);
}

/** Mapeia o rótulo de país usado na landing (pt/en/es) para ISO-2. */
const COUNTRY_LABEL_TO_ISO = [
    'brasil' => 'BR', 'brazil' => 'BR',
    'portugal' => 'PT',
    'angola' => 'AO',
    'moçambique' => 'MZ', 'mocambique' => 'MZ', 'mozambique' => 'MZ',
    'espanha' => 'ES', 'spain' => 'ES', 'españa' => 'ES',
    'estados unidos' => 'US', 'united states' => 'US',
    'reino unido' => 'GB', 'united kingdom' => 'GB',
    'frança' => 'FR', 'franca' => 'FR', 'france' => 'FR', 'francia' => 'FR',
    'itália' => 'IT', 'italia' => 'IT', 'italy' => 'IT',
    'alemanha' => 'DE', 'germany' => 'DE', 'alemania' => 'DE',
    'cabo verde' => 'CV', 'cape verde' => 'CV',
    'guiné-bissau' => 'GW', 'guine-bissau' => 'GW', 'guinea-bissau' => 'GW',
    'são tomé e príncipe' => 'ST', 'sao tome e principe' => 'ST',
    'timor-leste' => 'TL', 'east timor' => 'TL',
    'argentina' => 'AR', 'chile' => 'CL', 'colômbia' => 'CO', 'colombia' => 'CO',
    'méxico' => 'MX', 'mexico' => 'MX', 'peru' => 'PE', 'perú' => 'PE',
    'uruguai' => 'UY', 'uruguay' => 'UY', 'paraguai' => 'PY', 'paraguay' => 'PY',
    'suíça' => 'CH', 'suica' => 'CH', 'switzerland' => 'CH',
    'irlanda' => 'IE', 'ireland' => 'IE',
    'polônia' => 'PL', 'polonia' => 'PL', 'poland' => 'PL',
];

/**
 * Último recurso: deduz o país pelo DDI. Só cobre os prefixos relevantes para
 * o tráfego desta landing — o resto cai em '' e o CRM decide.
 */
function country_from_dial_code(string $phone): string
{
    $digits = preg_replace('/[^\d]/', '', $phone) ?? '';
    if ($digits === '') {
        return '';
    }
    // Prefixos mais longos primeiro, para 351 não ser confundido com 35.
    static $dial = [
        '238' => 'CV', '239' => 'ST', '244' => 'AO', '245' => 'GW', '258' => 'MZ',
        '351' => 'PT', '352' => 'LU', '353' => 'IE', '355' => 'AL', '358' => 'FI',
        '380' => 'UA', '420' => 'CZ', '670' => 'TL',
        '34' => 'ES', '39' => 'IT', '41' => 'CH', '43' => 'AT', '44' => 'GB',
        '48' => 'PL', '49' => 'DE', '31' => 'NL', '32' => 'BE', '33' => 'FR',
        '51' => 'PE', '52' => 'MX', '54' => 'AR', '55' => 'BR', '56' => 'CL',
        '57' => 'CO', '58' => 'VE', '61' => 'AU', '81' => 'JP', '86' => 'CN',
        '91' => 'IN', '98' => 'IR',
        '595' => 'PY', '598' => 'UY',
        '1' => 'US', '7' => 'RU',
    ];
    foreach ([3, 2, 1] as $len) {
        $prefix = substr($digits, 0, $len);
        if (isset($dial[$prefix])) {
            return $dial[$prefix];
        }
    }
    return '';
}
