yazrkisvs3vvvsv33svrhetrjsvyvsvvyjsvsvs
tavsuysvssvvjvvyjyjkvsvqd
qfogsvvsvsegjdgdfgdskhgdksvqsvshdqsd
<?php
// api/google-ads-create.php — Creación de campañas Google Ads con asistencia IA
header('Content-Type: application/json');
require_once __DIR__ . '/../config.php';
require_once __DIR__ . '/../db.php';
require_once __DIR__ . '/../auth.php';
require_once __DIR__ . '/../credentials.php';
dm_require_admin();

$action    = $_GET['action'] ?? '';
$client_id = (int)($_GET['client_id'] ?? 0);
if (!$client_id) { echo json_encode(['ok'=>false,'error'=>'client_id requerido']); exit; }
dm_require_client_access($client_id);

$input = json_decode(file_get_contents('php://input'), true) ?? [];

// ── Credenciales Google Ads ──────────────────────────────────────────────
$creds         = dm_get_credentials($client_id, 'google');
$dev_token     = defined('GOOGLE_DEV_TOKEN')     ? GOOGLE_DEV_TOKEN     : '';
$customer_id   = $creds['customer_id']           ?? (defined('GOOGLE_CUSTOMER_ID') ? GOOGLE_CUSTOMER_ID : '');
$mcc_id        = $creds['mcc_id']                ?? (defined('GOOGLE_MCC_ID')      ? GOOGLE_MCC_ID      : '');
$refresh_token = defined('GOOGLE_REFRESH_TOKEN') ? GOOGLE_REFRESH_TOKEN : '';
$oauth_cid     = defined('GOOGLE_CLIENT_ID')     ? GOOGLE_CLIENT_ID     : '';
$oauth_cs      = defined('GOOGLE_CLIENT_SECRET') ? GOOGLE_CLIENT_SECRET : '';

$customer_id = preg_replace('/[^0-9]/', '', $customer_id);
$mcc_id      = preg_replace('/[^0-9]/', '', $mcc_id);

if (!$customer_id) { echo json_encode(['ok'=>false,'error'=>'Customer ID no configurado para este cliente']); exit; }

// ── Access token ─────────────────────────────────────────────────────────
$access_token = '';
if ($refresh_token && $oauth_cid && $oauth_cs) {
    $ch = curl_init('https://oauth2.googleapis.com/token');
    curl_setopt_array($ch,[CURLOPT_RETURNTRANSFER=>true,CURLOPT_POST=>true,CURLOPT_SSL_VERIFYPEER=>false,CURLOPT_TIMEOUT=>15,
        CURLOPT_POSTFIELDS=>http_build_query(['client_id'=>$oauth_cid,'client_secret'=>$oauth_cs,'refresh_token'=>$refresh_token,'grant_type'=>'refresh_token'])]);
    $res=json_decode(curl_exec($ch),true); curl_close($ch);
    $access_token = $res['access_token'] ?? '';
}
if (!$access_token) { echo json_encode(['ok'=>false,'error'=>'No se pudo obtener access token. Reconecta Google Ads.']); exit; }

// ── Helper: mutate ────────────────────────────────────────────────────────
function gads_mutate(string $resource, array $ops, string $cid, string $dev, string $at, string $mcc): array {
    $url = "https://googleads.googleapis.com/v20/customers/{$cid}/{$resource}:mutate";
    $h   = ['Content-Type: application/json','Authorization: Bearer '.$at,'developer-token: '.$dev];
    if ($mcc && $mcc !== $cid) $h[] = 'login-customer-id: '.$mcc;
    $ch = curl_init($url);
    curl_setopt_array($ch,[CURLOPT_RETURNTRANSFER=>true,CURLOPT_POST=>true,
        CURLOPT_POSTFIELDS=>json_encode(['operations'=>$ops]),
        CURLOPT_HTTPHEADER=>$h,CURLOPT_TIMEOUT=>30,CURLOPT_SSL_VERIFYPEER=>false]);
    $raw=curl_exec($ch); $http=curl_getinfo($ch,CURLINFO_HTTP_CODE); curl_close($ch);
    if ($http!==200){ $e=json_decode($raw,true); return ['_err'=>$e['error']['message']??"HTTP $http",'_raw'=>$e]; }
    return json_decode($raw,true)??[];
}

// ════════════════════════════════════════════════════════════
switch ($action) {

// ── PASO 1: IA genera la estructura de campaña ───────────────────────────
case 'ai_plan':
    dm_csrf_verify();
    $brief   = trim($input['brief']   ?? '');
    $type    = $input['type']   ?? 'SEARCH';
    $goal    = $input['goal']   ?? 'LEADS';
    $url     = trim($input['url']     ?? '');
    $context = trim($input['context'] ?? '');

    if (!$brief) { echo json_encode(['ok'=>false,'error'=>'El brief está vacío']); exit; }

    $goal_lbl = ['LEADS'=>'generación de leads y contactos','SALES'=>'ventas directas','TRAFFIC'=>'tráfico al sitio web','AWARENESS'=>'reconocimiento de marca'];
    $type_lbl = ['SEARCH'=>'Red de Búsqueda (Search)','PERFORMANCE_MAX'=>'Performance Max','DISPLAY'=>'Red de Display'];

    $prompt = "Eres un experto certificado en Google Ads con enfoque en el mercado colombiano.
Crea la estructura completa para una campaña de {$type_lbl[$type]} con objetivo de {$goal_lbl[$goal]}.

URL destino: {$url}

Contexto del negocio (si lo hay):
{$context}

Brief específico de esta campaña:
{$brief}

Responde ÚNICAMENTE con este JSON, sin texto adicional ni markdown:
{
  \"campaign_name\": \"Nombre descriptivo y corto\",
  \"budget_daily_cop\": 150000,
  \"bidding\": \"MAXIMIZE_CLICKS\",
  \"bidding_reason\": \"Explicación breve en español\",
  \"ad_groups\": [
    {
      \"name\": \"Nombre del grupo\",
      \"theme\": \"Tema del grupo en una frase\",
      \"keywords\": [
        {\"text\": \"keyword aquí\", \"match\": \"B\"},
        {\"text\": \"keyword aquí\", \"match\": \"P\"},
        {\"text\": \"keyword exacta\", \"match\": \"E\"}
      ],
      \"headlines\": [\"Título 1\", \"Título 2\", \"Título 3\"],
      \"descriptions\": [\"Descripción 1\", \"Descripción 2\"]
    }
  ]
}

Reglas estrictas:
- 2 a 3 grupos de anuncios con temas distintos y complementarios
- 8 a 12 keywords por grupo: mezcla de B (amplia), P (frase) y E (exacta)
- Headlines: máximo 30 caracteres CADA UNO, sin punto al final, inicial mayúscula
- Descriptions: máximo 90 caracteres CADA UNO, incluir llamado a la acción
- Keywords en español colombiano, sin puntuación especial
- Budget en COP según el tipo de negocio (mínimo 50000, máximo 500000)
- BIDDING: MAXIMIZE_CLICKS si objetivo es tráfico/leads sin historial previo; MAXIMIZE_CONVERSIONS si hay datos";

    $api_key = defined('CLAUDE_API_KEY') ? CLAUDE_API_KEY : '';
    if (!$api_key) { echo json_encode(['ok'=>false,'error'=>'CLAUDE_API_KEY no configurada en config.php']); exit; }

    $ch = curl_init('https://api.anthropic.com/v1/messages');
    curl_setopt_array($ch,[CURLOPT_RETURNTRANSFER=>true,CURLOPT_POST=>true,CURLOPT_SSL_VERIFYPEER=>false,CURLOPT_TIMEOUT=>60,
        CURLOPT_HTTPHEADER=>['x-api-key: '.$api_key,'anthropic-version: 2023-06-01','Content-Type: application/json'],
        CURLOPT_POSTFIELDS=>json_encode(['model'=>'claude-haiku-4-5-20251001','max_tokens'=>3000,
            'messages'=>[['role'=>'user','content'=>$prompt]]])]);
    $raw = curl_exec($ch); $http = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
    $res = json_decode($raw, true);

    // Detectar error de API Claude
    if ($http !== 200 || isset($res['error'])) {
        $err = $res['error']['message'] ?? ($res['message'] ?? "HTTP $http");
        echo json_encode(['ok'=>false,'error'=>'Error Claude API: '.$err]); exit;
    }

    $txt = trim($res['content'][0]['text'] ?? '');
    // Extraer JSON del response (puede venir con markdown ```json)
    if (preg_match('/```(?:json)?\s*(\{[\s\S]*?\})\s*```/s', $txt, $m)) $txt = $m[1];
    elseif (preg_match('/\{[\s\S]*\}/s', $txt, $m)) $txt = $m[0];
    $plan = json_decode($txt, true);
    if (!$plan || !isset($plan['ad_groups']) || !count($plan['ad_groups'])) {
        echo json_encode(['ok'=>false,'error'=>'IA no pudo generar la estructura. Intenta con un brief más específico.','raw'=>substr($txt,0,500)]);
        exit;
    }
    // Sanitize headline/description lengths
    foreach ($plan['ad_groups'] as &$ag) {
        $ag['headlines']    = array_map(fn($h)=>substr(trim($h),0,30), $ag['headlines']??[]);
        $ag['descriptions'] = array_map(fn($d)=>substr(trim($d),0,90), $ag['descriptions']??[]);
    }
    echo json_encode(['ok'=>true,'plan'=>$plan]);
    break;

// ── PASO 2: Crear campaña completa en Google Ads API ────────────────────
case 'create_full':
    dm_csrf_verify();
    $plan         = $input['plan']         ?? [];
    $type         = $input['type']         ?? 'SEARCH';
    $url          = trim($input['url']     ?? '');
    $location_ids = $input['location_ids'] ?? [2170];
    $start_date   = preg_replace('/[^0-9\-]/','',$input['start_date']??date('Y-m-d'));

    if (!$plan || empty($plan['campaign_name'])) {
        echo json_encode(['ok'=>false,'error'=>'Plan de campaña inválido o vacío']); exit;
    }
    if (!$url) { echo json_encode(['ok'=>false,'error'=>'URL de destino requerida']); exit; }

    $budget_cop    = (int)($plan['budget_daily_cop'] ?? 150000);
    $budget_micros = $budget_cop * 1000000;
    $camp_type_map = ['SEARCH'=>'SEARCH','PERFORMANCE_MAX'=>'PERFORMANCE_MAX','DISPLAY'=>'DISPLAY'];
    $camp_type     = $camp_type_map[$type] ?? 'SEARCH';

    // ── 1. Presupuesto ──────────────────────────────────────────────────
    $bud_res = gads_mutate('campaignBudgets',[['create'=>[
        'name'           => $plan['campaign_name'].' — Presupuesto',
        'amountMicros'   => $budget_micros,
        'deliveryMethod' => 'STANDARD',
    ]]],$customer_id,$dev_token,$access_token,$mcc_id);
    if (isset($bud_res['_err'])) { echo json_encode(['ok'=>false,'error'=>'Error al crear presupuesto: '.$bud_res['_err']]); exit; }
    $budget_rn = $bud_res['results'][0]['resourceName'] ?? '';

    // ── 2. Campaña ───────────────────────────────────────────────────────
    $bidding = $plan['bidding'] ?? 'MAXIMIZE_CLICKS';
    $camp_obj = [
        'name'                    => $plan['campaign_name'],
        'status'                  => 'PAUSED',
        'advertisingChannelType'  => $camp_type,
        'campaignBudget'          => $budget_rn,
    ];
    if ($camp_type === 'SEARCH') {
        $camp_obj['networkSettings'] = [
            'targetGoogleSearch'   => true,
            'targetSearchNetwork'  => true,
            'targetContentNetwork' => false,
        ];
    }
    // Estrategia de puja
    if ($bidding === 'MAXIMIZE_CLICKS')          $camp_obj['maximizeClicks']       = new stdClass();
    elseif ($bidding === 'MAXIMIZE_CONVERSIONS') $camp_obj['maximizeConversions']  = new stdClass();
    elseif ($bidding === 'TARGET_CPA')           $camp_obj['targetCpa']            = ['targetCpaMicros'=>50000000];
    elseif ($bidding === 'MANUAL_CPC')           $camp_obj['manualCpc']            = ['enhancedCpcEnabled'=>false];
    else                                         $camp_obj['maximizeClicks']       = new stdClass();

    $camp_res = gads_mutate('campaigns',[['create'=>$camp_obj]],$customer_id,$dev_token,$access_token,$mcc_id);
    if (isset($camp_res['_err'])) { echo json_encode(['ok'=>false,'error'=>'Error al crear campaña: '.$camp_res['_err'],'details'=>$camp_res['_raw']]); exit; }
    $camp_rn = $camp_res['results'][0]['resourceName'] ?? '';
    $camp_id = preg_replace('/[^0-9]/','',$camp_rn);

    // ── 3. Segmentación por ubicación ────────────────────────────────────
    if (!empty($location_ids)) {
        $loc_ops = array_values(array_filter(array_map(function($lid) use ($camp_rn) {
            $lid = (int)$lid;
            if (!$lid) return null;
            return ['create'=>['campaign'=>$camp_rn,'location'=>['geoTargetConstant'=>"geoTargetConstants/{$lid}"],'negative'=>false]];
        }, $location_ids)));
        if ($loc_ops) gads_mutate('campaignCriteria',$loc_ops,$customer_id,$dev_token,$access_token,$mcc_id);
    }

    // ── 4. Idioma: español ───────────────────────────────────────────────
    gads_mutate('campaignCriteria',[
        ['create'=>['campaign'=>$camp_rn,'language'=>['languageConstant'=>'languageConstants/1000']]]
    ],$customer_id,$dev_token,$access_token,$mcc_id);

    // ── 5. Grupos de anuncios + keywords + RSAs ───────────────────────────
    $results = [];
    foreach (($plan['ad_groups'] ?? []) as $idx => $ag) {
        $ag_name = trim($ag['name'] ?? "Grupo ".($idx+1));

        // Crear ad group
        $ag_obj = ['name'=>$ag_name,'campaign'=>$camp_rn,'status'=>'ENABLED','type'=>'SEARCH_STANDARD','cpcBidMicros'=>500000];
        $ag_res = gads_mutate('adGroups',[['create'=>$ag_obj]],$customer_id,$dev_token,$access_token,$mcc_id);
        if (isset($ag_res['_err'])) { $results[]=['group'=>$ag_name,'error'=>$ag_res['_err']]; continue; }
        $ag_rn = $ag_res['results'][0]['resourceName'] ?? '';

        // Keywords
        $kw_ops = [];
        foreach ($ag['keywords'] ?? [] as $kw) {
            $text  = trim($kw['text'] ?? ''); if (!$text) continue;
            $match_map = ['B'=>'BROAD','P'=>'PHRASE','E'=>'EXACT','BROAD'=>'BROAD','PHRASE'=>'PHRASE','EXACT'=>'EXACT'];
            $match = $match_map[strtoupper($kw['match']??'B')] ?? 'BROAD';
            $kw_ops[] = ['create'=>['adGroup'=>$ag_rn,'keyword'=>['text'=>$text,'matchType'=>$match],'status'=>'ENABLED']];
        }
        if ($kw_ops) gads_mutate('adGroupCriteria',$kw_ops,$customer_id,$dev_token,$access_token,$mcc_id);

        // RSA
        $headlines = array_values(array_filter(array_map(fn($h)=>['text'=>substr(trim($h),0,30)], $ag['headlines']??[])));
        $descs     = array_values(array_filter(array_map(fn($d)=>['text'=>substr(trim($d),0,90)], $ag['descriptions']??[])));
        $ad_ok = false;
        if (count($headlines) >= 3 && count($descs) >= 2) {
            $rsa_res = gads_mutate('adGroupAds',[['create'=>[
                'adGroup' => $ag_rn,
                'status'  => 'ENABLED',
                'ad'      => ['responsiveSearchAd'=>['headlines'=>$headlines,'descriptions'=>$descs],'finalUrls'=>[$url]],
            ]]],$customer_id,$dev_token,$access_token,$mcc_id);
            $ad_ok = !isset($rsa_res['_err']);
        }
        $results[] = ['group'=>$ag_name,'keywords'=>count($kw_ops),'ad_created'=>$ad_ok];
    }

    echo json_encode([
        'ok'          => true,
        'campaign_id' => $camp_id,
        'campaign_rn' => $camp_rn,
        'results'     => $results,
        'message'     => "Campaña '{$plan['campaign_name']}' creada en pausa. Revísala en Google Ads antes de activar.",
    ]);
    break;

default:
    echo json_encode(['ok'=>false,'error'=>'Acción no válida']);
}
