<?php
namespace controls;

class CampaignTerms
{
  static private function is_google_ads_debug_enabled()
  {
    return \services\GoogleAdsApi::get_setting( 'google_ads_debug_enabled' ) !== '0';
  }

  static private function with_optional_debug( $payload, $debug_data )
  {
    if ( self::is_google_ads_debug_enabled() )
    {
      $payload['debug'] = $debug_data;
    }

    return $payload;
  }

  static private function normalize_ai_recommendations( $raw_json, $rows_by_id )
  {
    $decoded = json_decode( (string) $raw_json, true );
    $items = is_array( $decoded ) && isset( $decoded['items'] ) && is_array( $decoded['items'] ) ? $decoded['items'] : [];
    $recommendations = [];

    foreach ( $items as $item )
    {
      $id = (int) ( $item['id'] ?? 0 );
      if ( $id <= 0 || !isset( $rows_by_id[$id] ) )
      {
        continue;
      }

      $action_raw = strtolower( trim( (string) ( $item['action'] ?? '' ) ) );
      $action = $action_raw === 'exclude' ? 'exclude' : 'keep';
      $reason = trim( (string) ( $item['reason'] ?? '' ) );
      if ( $reason === '' )
      {
        $reason = $action === 'exclude' ? 'Niska trafnosc lub slabe wyniki.' : 'Fraza zostaje bez zmian.';
      }

      if ( function_exists( 'mb_substr' ) )
      {
        $reason = mb_substr( $reason, 0, 120 );
      }
      else
      {
        $reason = substr( $reason, 0, 120 );
      }

      $row = $rows_by_id[$id];

      $recommendations[] = [
        'id' => $id,
        'search_term_id' => $id,
        'phrase' => trim( (string) ( $row['search_term'] ?? ( $item['phrase'] ?? '' ) ) ),
        'action' => $action,
        'reason' => $reason,
        'ad_group_name' => (string) ( $row['ad_group_name'] ?? '' ),
        'clicks_all_time' => (float) ( $row['clicks_all_time'] ?? 0 ),
        'cost_all_time' => (float) ( $row['cost_all_time'] ?? 0 ),
        'conversions_all_time' => (float) ( $row['conversions_all_time'] ?? 0 ),
        'conversion_value_all_time' => (float) ( $row['conversion_value_all_time'] ?? 0 ),
        'roas_all_time' => (float) ( $row['roas_all_time'] ?? 0 )
      ];
    }

    return $recommendations;
  }

  static public function main_view()
  {
    return \Tpl::view( 'campaign_terms/main_view', [
      'clients' => \factory\Campaigns::get_clients(),
    ] );
  }

  static public function get_campaigns_list()
  {
    $client_id = (int) \S::get( 'client_id' );
    echo json_encode( [ 'campaigns' => \factory\Campaigns::get_campaigns_list( $client_id, true ) ] );
    exit;
  }

  static public function get_campaign_ad_groups()
  {
    $campaign_id = (int) \S::get( 'campaign_id' );

    if ( $campaign_id <= 0 )
    {
      echo json_encode( [ 'ad_groups' => [] ] );
      exit;
    }

    echo json_encode( [ 'ad_groups' => \factory\Campaigns::get_campaign_ad_groups( $campaign_id ) ] );
    exit;
  }

  static public function get_campaign_phrase_details()
  {
    $campaign_id = (int) \S::get( 'campaign_id' );
    $ad_group_id = (int) \S::get( 'ad_group_id' );

    if ( $campaign_id <= 0 )
    {
      echo json_encode( [ 'search_terms' => [], 'negative_keywords' => [], 'keywords' => [] ] );
      exit;
    }

    echo json_encode( [
      'search_terms' => \factory\Campaigns::get_campaign_search_terms( $campaign_id, $ad_group_id ),
      'negative_keywords' => \factory\Campaigns::get_campaign_negative_keywords( $campaign_id, $ad_group_id ),
      'keywords' => \factory\Campaigns::get_campaign_keywords( $campaign_id, $ad_group_id )
    ] );
    exit;
  }

  static public function add_negative_keyword()
  {
    $search_term_id = (int) \S::get( 'search_term_id' );
    $match_type = strtoupper( trim( (string) \S::get( 'match_type' ) ) );
    $scope = strtolower( trim( (string) \S::get( 'scope' ) ) );
    $manual_keyword_text = trim( (string) \S::get( 'keyword_text' ) );

    if ( $search_term_id <= 0 )
    {
      echo json_encode( self::with_optional_debug( [
        'success' => false,
        'message' => 'Nie podano frazy do wykluczenia.',
      ], [
        'search_term_id' => $search_term_id,
        'match_type_raw' => (string) \S::get( 'match_type' ),
        'scope_raw' => (string) \S::get( 'scope' ),
        'manual_keyword_text' => $manual_keyword_text
      ] ) );
      exit;
    }

    if ( !in_array( $match_type, [ 'PHRASE', 'EXACT', 'BROAD' ], true ) )
    {
      $match_type = 'PHRASE';
    }
    if ( !in_array( $scope, [ 'campaign', 'ad_group' ], true ) )
    {
      $scope = 'campaign';
    }

    $context = \factory\Campaigns::get_search_term_context( $search_term_id );
    if ( !$context )
    {
      echo json_encode( self::with_optional_debug( [
        'success' => false,
        'message' => 'Nie znaleziono danych frazy.',
      ], [
        'search_term_id' => $search_term_id,
        'manual_keyword_text' => $manual_keyword_text,
        'scope' => $scope,
        'match_type' => $match_type
      ] ) );
      exit;
    }

    $customer_id = trim( (string) ( $context['google_ads_customer_id'] ?? '' ) );
    $campaign_external_id = trim( (string) ( $context['external_campaign_id'] ?? '' ) );
    $ad_group_external_id = trim( (string) ( $context['external_ad_group_id'] ?? '' ) );
    $context_keyword_text = trim( (string) ( $context['search_term'] ?? '' ) );
    $keyword_text = $manual_keyword_text !== '' ? $manual_keyword_text : $context_keyword_text;
    $keyword_source = $manual_keyword_text !== '' ? 'manual' : 'search_term';

    $missing_data = ( $customer_id === '' || $keyword_text === '' );
    if ( $scope === 'campaign' && $campaign_external_id === '' )
    {
      $missing_data = true;
    }
    if ( $scope === 'ad_group' && $ad_group_external_id === '' )
    {
      $missing_data = true;
    }

    if ( $missing_data )
    {
      echo json_encode( self::with_optional_debug( [
        'success' => false,
        'message' => 'Brak wymaganych danych Google Ads dla tej frazy.',
      ], [
        'customer_id' => $customer_id,
        'campaign_external_id' => $campaign_external_id,
        'ad_group_external_id' => $ad_group_external_id,
        'keyword_text' => $keyword_text,
        'keyword_source' => $keyword_source,
        'scope' => $scope,
        'context' => $context
      ] ) );
      exit;
    }

    $api = new \services\GoogleAdsApi();
    if ( !$api -> is_configured() )
    {
      echo json_encode( self::with_optional_debug( [
        'success' => false,
        'message' => 'Google Ads API nie jest skonfigurowane.',
      ], [
        'search_term_id' => $search_term_id,
        'customer_id' => $customer_id,
        'campaign_external_id' => $campaign_external_id,
        'ad_group_external_id' => $ad_group_external_id,
        'keyword_text' => $keyword_text,
        'match_type' => $match_type,
        'scope' => $scope
      ] ) );
      exit;
    }

    if ( $scope === 'campaign' )
    {
      $api_result = $api -> add_negative_keyword_to_campaign( $customer_id, $campaign_external_id, $keyword_text, $match_type );
    }
    else
    {
      $api_result = $api -> add_negative_keyword_to_ad_group( $customer_id, $ad_group_external_id, $keyword_text, $match_type );
    }

    if ( !( $api_result['success'] ?? false ) )
    {
      $last_error = \services\GoogleAdsApi::get_setting( 'google_ads_last_error' );
      echo json_encode( self::with_optional_debug( [
        'success' => false,
        'message' => 'Nie udalo sie zapisac frazy wykluczajacej w Google Ads.',
        'error' => $last_error
      ], [
        'customer_id' => $customer_id,
        'campaign_external_id' => $campaign_external_id,
        'ad_group_external_id' => $ad_group_external_id,
        'keyword_text' => $keyword_text,
        'keyword_source' => $keyword_source,
        'match_type' => $match_type,
        'scope' => $scope,
        'api_result' => $api_result
      ] ) );
      exit;
    }

    $verification = $api_result['verification'] ?? null;
    $verification_found = true;
    if ( is_array( $verification ) && array_key_exists( 'found', $verification ) )
    {
      $verification_found = (bool) $verification['found'];
    }

    if ( !$verification_found && !( $api_result['duplicate'] ?? false ) )
    {
      echo json_encode( self::with_optional_debug( [
        'success' => false,
        'message' => 'Google Ads API nie potwierdzilo dodania frazy po operacji create.',
      ], [
        'customer_id' => $customer_id,
        'campaign_external_id' => $campaign_external_id,
        'ad_group_external_id' => $ad_group_external_id,
        'keyword_text' => $keyword_text,
        'keyword_source' => $keyword_source,
        'match_type' => $match_type,
        'scope' => $scope,
        'api_result' => $api_result,
        'verification' => $verification
      ] ) );
      exit;
    }

    \factory\Campaigns::upsert_campaign_negative_keyword(
      (int) $context['db_campaign_id'],
      $scope === 'campaign' ? null : (int) $context['db_ad_group_id'],
      $scope,
      $keyword_text,
      $match_type
    );

    $scope_label = $scope === 'campaign' ? 'kampanii' : 'grupy reklam';

    echo json_encode( self::with_optional_debug( [
      'success' => true,
      'message' => ( $api_result['duplicate'] ?? false ) ? 'Fraza byla juz wykluczona na poziomie ' . $scope_label . '.' : 'Fraza zostala dodana do wykluczajacych na poziomie ' . $scope_label . '.',
      'duplicate' => (bool) ( $api_result['duplicate'] ?? false ),
      'match_type' => $match_type,
      'scope' => $scope
    ], [
      'customer_id' => $customer_id,
      'campaign_external_id' => $campaign_external_id,
      'ad_group_external_id' => $ad_group_external_id,
      'keyword_text' => $keyword_text,
      'keyword_source' => $keyword_source,
      'scope' => $scope,
      'api_response' => $api_result['response'] ?? null,
      'sent_operation' => $api_result['sent_operation'] ?? null,
      'verification' => $api_result['verification'] ?? null
    ] ) );
    exit;
  }

  static public function analyze_search_terms_with_ai()
  {
    $campaign_id = (int) \S::get( 'campaign_id' );
    $ad_group_id = (int) \S::get( 'ad_group_id' );
    $search_term_ids_raw = \S::get( 'search_term_ids' );

    if ( $campaign_id <= 0 )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Wybierz kampanie.' ] );
      exit;
    }

    if ( \services\GoogleAdsApi::get_setting( 'openai_enabled' ) === '0' )
    {
      echo json_encode( [ 'success' => false, 'message' => 'OpenAI jest wylaczone. Wlacz je w Ustawieniach.' ] );
      exit;
    }

    if ( !\services\OpenAiApi::is_configured() )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Klucz API OpenAI nie jest skonfigurowany. Przejdz do Ustawien.' ] );
      exit;
    }

    $rows = \factory\Campaigns::get_campaign_search_terms( $campaign_id, $ad_group_id );

    $ids_filter = [];
    if ( is_array( $search_term_ids_raw ) )
    {
      foreach ( $search_term_ids_raw as $id_raw )
      {
        $id = (int) $id_raw;
        if ( $id > 0 )
        {
          $ids_filter[$id] = true;
        }
      }
    }
    elseif ( $search_term_ids_raw !== null && $search_term_ids_raw !== '' )
    {
      $id = (int) $search_term_ids_raw;
      if ( $id > 0 )
      {
        $ids_filter[$id] = true;
      }
    }

    if ( !empty( $ids_filter ) )
    {
      $rows = array_values( array_filter( $rows, function( $row ) use ( $ids_filter )
      {
        $id = (int) ( $row['id'] ?? 0 );
        return $id > 0 && isset( $ids_filter[$id] );
      } ) );
    }

    if ( empty( $rows ) )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Brak fraz do analizy.' ] );
      exit;
    }

    $rows = array_slice( $rows, 0, 150 );

    $rows_by_id = [];
    foreach ( $rows as $row )
    {
      $id = (int) ( $row['id'] ?? 0 );
      if ( $id > 0 )
      {
        $rows_by_id[$id] = $row;
      }
    }

    $campaign_name = '';
    $campaign_type = '';
    if ( !empty( $rows ) )
    {
      $campaign_name = trim( (string) ( $rows[0]['campaign_name'] ?? '' ) );
      $campaign_type = trim( (string) ( $rows[0]['advertising_channel_type'] ?? '' ) );
    }

    $ad_group_name = '';
    if ( $ad_group_id > 0 && !empty( $rows ) )
    {
      $ad_group_name = trim( (string) ( $rows[0]['ad_group_name'] ?? '' ) );
    }

    $result = \services\OpenAiApi::suggest_negative_keywords_to_exclude( $rows, [
      'campaign_name' => $campaign_name,
      'campaign_type' => $campaign_type,
      'ad_group_name' => $ad_group_name,
      'ad_group_id' => $ad_group_id
    ] );

    if ( ( $result['status'] ?? 'error' ) !== 'ok' )
    {
      echo json_encode( [
        'success' => false,
        'message' => (string) ( $result['message'] ?? 'Blad analizy OpenAI.' )
      ] );
      exit;
    }

    $raw_json = (string) ( $result['suggestion'] ?? '' );
    $recommendations = self::normalize_ai_recommendations( $raw_json, $rows_by_id );
    $exclude_count = count( array_filter( $recommendations, function( $item )
    {
      return ( $item['action'] ?? '' ) === 'exclude';
    } ) );

    if ( empty( $recommendations ) )
    {
      echo json_encode( [
        'success' => false,
        'message' => 'Nie udalo sie sparsowac odpowiedzi AI.',
        'raw' => $raw_json
      ] );
      exit;
    }

    echo json_encode( [
      'success' => true,
      'message' => 'Analiza zakonczona. Proponowane wykluczenia: ' . $exclude_count . '.',
      'analyzed_count' => count( $rows ),
      'exclude_count' => $exclude_count,
      'recommendations' => $recommendations
    ] );
    exit;
  }

  static private function delete_negative_keyword_row( $negative_keyword_id )
  {
    $negative_keyword_id = (int) $negative_keyword_id;

    if ( $negative_keyword_id <= 0 )
    {
      return [ 'success' => false, 'message' => 'Nie podano frazy do usuniecia.' ];
    }

    $context = \factory\Campaigns::get_negative_keyword_context( $negative_keyword_id );
    if ( !$context )
    {
      return [ 'success' => false, 'message' => 'Nie znaleziono danych frazy wykluczajacej.' ];
    }

    $customer_id = trim( (string) ( $context['google_ads_customer_id'] ?? '' ) );
    $scope = strtolower( trim( (string) ( $context['scope'] ?? 'campaign' ) ) );
    $match_type = strtoupper( trim( (string) ( $context['match_type'] ?? 'PHRASE' ) ) );
    $keyword_text = trim( (string) ( $context['keyword_text'] ?? '' ) );
    $campaign_external_id = trim( (string) ( $context['external_campaign_id'] ?? '' ) );
    $ad_group_external_id = trim( (string) ( $context['external_ad_group_id'] ?? '' ) );

    if ( !in_array( $scope, [ 'campaign', 'ad_group' ], true ) )
    {
      $scope = 'campaign';
    }

    $missing_data = ( $customer_id === '' || $keyword_text === '' );
    if ( $scope === 'campaign' && $campaign_external_id === '' )
    {
      $missing_data = true;
    }
    if ( $scope === 'ad_group' && $ad_group_external_id === '' )
    {
      $missing_data = true;
    }

    if ( $missing_data )
    {
      return self::with_optional_debug( [
        'success' => false,
        'message' => 'Brak wymaganych danych Google Ads dla tej frazy.'
      ], [
        'context' => $context
      ] );
    }

    $api = new \services\GoogleAdsApi();
    if ( !$api -> is_configured() )
    {
      return self::with_optional_debug( [
        'success' => false,
        'message' => 'Google Ads API nie jest skonfigurowane.'
      ], [
        'customer_id' => $customer_id,
        'scope' => $scope,
        'campaign_external_id' => $campaign_external_id,
        'ad_group_external_id' => $ad_group_external_id,
        'keyword_text' => $keyword_text,
        'match_type' => $match_type
      ] );
    }

    if ( $scope === 'campaign' )
    {
      $api_result = $api -> remove_negative_keyword_from_campaign( $customer_id, $campaign_external_id, $keyword_text, $match_type );
    }
    else
    {
      $api_result = $api -> remove_negative_keyword_from_ad_group( $customer_id, $ad_group_external_id, $keyword_text, $match_type );
    }

    if ( !( $api_result['success'] ?? false ) )
    {
      $last_error = \services\GoogleAdsApi::get_setting( 'google_ads_last_error' );
      return self::with_optional_debug( [
        'success' => false,
        'message' => 'Nie udalo sie usunac frazy wykluczajacej w Google Ads.',
        'error' => $last_error
      ], [
        'customer_id' => $customer_id,
        'scope' => $scope,
        'campaign_external_id' => $campaign_external_id,
        'ad_group_external_id' => $ad_group_external_id,
        'keyword_text' => $keyword_text,
        'match_type' => $match_type,
        'api_result' => $api_result
      ] );
    }

    \factory\Campaigns::delete_campaign_negative_keyword( $negative_keyword_id );

    $removed = (int) ( $api_result['removed'] ?? 0 );
    $scope_label = $scope === 'campaign' ? 'kampanii' : 'grupy reklam';
    $message = $removed > 0
      ? 'Fraza zostala usunieta z wykluczajacych na poziomie ' . $scope_label . '.'
      : 'Fraza nie byla juz obecna w Google Ads. Usunieto lokalny wpis.';

    return self::with_optional_debug( [
      'success' => true,
      'message' => $message,
      'removed' => $removed,
      'negative_keyword_id' => $negative_keyword_id
    ], [
      'customer_id' => $customer_id,
      'scope' => $scope,
      'campaign_external_id' => $campaign_external_id,
      'ad_group_external_id' => $ad_group_external_id,
      'keyword_text' => $keyword_text,
      'match_type' => $match_type,
      'api_result' => $api_result
    ] );
  }

  static public function delete_negative_keyword()
  {
    $negative_keyword_id = (int) \S::get( 'negative_keyword_id' );
    $result = self::delete_negative_keyword_row( $negative_keyword_id );
    echo json_encode( $result );
    exit;
  }

  static public function delete_negative_keywords()
  {
    $negative_keyword_ids_raw = \S::get( 'negative_keyword_ids' );

    if ( !is_array( $negative_keyword_ids_raw ) )
    {
      $negative_keyword_ids_raw = [ $negative_keyword_ids_raw ];
    }

    $negative_keyword_ids = [];
    foreach ( $negative_keyword_ids_raw as $id_raw )
    {
      $id = (int) $id_raw;
      if ( $id > 0 )
      {
        $negative_keyword_ids[] = $id;
      }
    }

    $negative_keyword_ids = array_values( array_unique( $negative_keyword_ids ) );

    if ( empty( $negative_keyword_ids ) )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Nie podano fraz do usuniecia.' ] );
      exit;
    }

    $deleted_count = 0;
    $failed = [];
    $debug = [];
    $total_count = count( $negative_keyword_ids );

    foreach ( $negative_keyword_ids as $negative_keyword_id )
    {
      $result = self::delete_negative_keyword_row( $negative_keyword_id );

      if ( $result['success'] ?? false )
      {
        $deleted_count++;

        $debug[] = [
          'id' => $negative_keyword_id,
          'success' => true,
          'message' => (string) ( $result['message'] ?? '' ),
          'debug' => $result['debug'] ?? null
        ];
        continue;
      }

      $failed[] = [
        'id' => $negative_keyword_id,
        'message' => (string) ( $result['message'] ?? 'Nieznany blad' ),
        'error' => (string) ( $result['error'] ?? '' ),
        'debug' => $result['debug'] ?? null
      ];

      $debug[] = [
        'id' => $negative_keyword_id,
        'success' => false,
        'message' => (string) ( $result['message'] ?? 'Nieznany blad' ),
        'error' => (string) ( $result['error'] ?? '' ),
        'debug' => $result['debug'] ?? null
      ];
    }

    $failed_count = count( $failed );

    if ( $deleted_count === $total_count )
    {
      $response = [
        'success' => true,
        'message' => 'Usunieto zaznaczone frazy wykluczajace (' . $deleted_count . ').',
        'deleted_count' => $deleted_count,
        'failed_count' => 0
      ];
      if ( self::is_google_ads_debug_enabled() )
      {
        $response['debug'] = $debug;
      }

      echo json_encode( $response );
      exit;
    }

    if ( $deleted_count > 0 )
    {
      $response = [
        'success' => true,
        'partial' => true,
        'message' => 'Usunieto ' . $deleted_count . ' z ' . $total_count . ' zaznaczonych fraz wykluczajacych.',
        'deleted_count' => $deleted_count,
        'failed_count' => $failed_count,
        'failed' => $failed
      ];
      if ( self::is_google_ads_debug_enabled() )
      {
        $response['debug'] = $debug;
      }

      echo json_encode( $response );
      exit;
    }

    $response = [
      'success' => false,
      'message' => 'Nie udalo sie usunac zaznaczonych fraz wykluczajacych.',
      'deleted_count' => 0,
      'failed_count' => $failed_count,
      'failed' => $failed
    ];
    if ( self::is_google_ads_debug_enabled() )
    {
      $response['debug'] = $debug;
    }

    echo json_encode( $response );
    exit;
  }

  static public function update_keyword_match_type()
  {
    $keyword_id = (int) \S::get( 'keyword_id' );
    $new_match_type = strtoupper( trim( (string) \S::get( 'new_match_type' ) ) );

    if ( $keyword_id <= 0 )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Nie podano frazy.' ] );
      exit;
    }

    if ( !in_array( $new_match_type, [ 'PHRASE', 'EXACT', 'BROAD' ], true ) )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Nieprawidlowy typ dopasowania.' ] );
      exit;
    }

    $context = \factory\Campaigns::get_keyword_context( $keyword_id );
    if ( !$context )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Nie znaleziono danych frazy.' ] );
      exit;
    }

    $old_match_type = strtoupper( trim( (string) ( $context['match_type'] ?? '' ) ) );
    if ( $old_match_type === $new_match_type )
    {
      echo json_encode( [ 'success' => true, 'message' => 'Dopasowanie jest juz ustawione na ' . $new_match_type . '.' ] );
      exit;
    }

    $customer_id = trim( (string) ( $context['google_ads_customer_id'] ?? '' ) );
    $ad_group_external_id = trim( (string) ( $context['external_ad_group_id'] ?? '' ) );
    $keyword_text = trim( (string) ( $context['keyword_text'] ?? '' ) );

    if ( $customer_id === '' || $ad_group_external_id === '' || $keyword_text === '' )
    {
      echo json_encode( self::with_optional_debug( [
        'success' => false,
        'message' => 'Brak wymaganych danych Google Ads dla tej frazy.',
      ], [ 'context' => $context ] ) );
      exit;
    }

    $api = new \services\GoogleAdsApi();
    if ( !$api -> is_configured() )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Google Ads API nie jest skonfigurowane.' ] );
      exit;
    }

    $api_result = $api -> update_keyword_match_type( $customer_id, $ad_group_external_id, $keyword_text, $old_match_type, $new_match_type );

    if ( !( $api_result['success'] ?? false ) )
    {
      $last_error = \services\GoogleAdsApi::get_setting( 'google_ads_last_error' );
      echo json_encode( self::with_optional_debug( [
        'success' => false,
        'message' => 'Nie udalo sie zmienic dopasowania frazy w Google Ads.',
        'error' => $last_error
      ], [
        'customer_id' => $customer_id,
        'ad_group_external_id' => $ad_group_external_id,
        'keyword_text' => $keyword_text,
        'old_match_type' => $old_match_type,
        'new_match_type' => $new_match_type,
        'api_result' => $api_result
      ] ) );
      exit;
    }

    \factory\Campaigns::update_keyword_match_type( $keyword_id, $new_match_type );

    echo json_encode( self::with_optional_debug( [
      'success' => true,
      'message' => 'Dopasowanie zmienione z ' . $old_match_type . ' na ' . $new_match_type . '.'
    ], [
      'customer_id' => $customer_id,
      'ad_group_external_id' => $ad_group_external_id,
      'keyword_text' => $keyword_text,
      'old_match_type' => $old_match_type,
      'new_match_type' => $new_match_type,
      'api_result' => $api_result
    ] ) );
    exit;
  }

  static public function add_keyword()
  {
    $campaign_id = (int) \S::get( 'campaign_id' );
    $ad_group_id = (int) \S::get( 'ad_group_id' );
    $keyword_text = trim( (string) \S::get( 'keyword_text' ) );
    $match_type = strtoupper( trim( (string) \S::get( 'match_type' ) ) );

    if ( $campaign_id <= 0 || $ad_group_id <= 0 )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Wybierz kampanie i grupe reklam.' ] );
      exit;
    }

    if ( $keyword_text === '' )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Wpisz fraze do dodania.' ] );
      exit;
    }

    if ( !in_array( $match_type, [ 'PHRASE', 'EXACT', 'BROAD' ], true ) )
    {
      $match_type = 'BROAD';
    }

    global $mdb;

    $campaign_data = $mdb -> query(
      'SELECT c.campaign_id AS external_campaign_id, cl.google_ads_customer_id
       FROM campaigns AS c
       INNER JOIN clients AS cl ON cl.id = c.client_id
       WHERE c.id = :campaign_id
       LIMIT 1',
      [ ':campaign_id' => $campaign_id ]
    ) -> fetch( \PDO::FETCH_ASSOC );

    if ( !$campaign_data )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Nie znaleziono kampanii.' ] );
      exit;
    }

    $ad_group_data = $mdb -> query(
      'SELECT ad_group_id AS external_ad_group_id FROM campaign_ad_groups WHERE id = :ad_group_id LIMIT 1',
      [ ':ad_group_id' => $ad_group_id ]
    ) -> fetch( \PDO::FETCH_ASSOC );

    if ( !$ad_group_data )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Nie znaleziono grupy reklam.' ] );
      exit;
    }

    $customer_id = trim( (string) ( $campaign_data['google_ads_customer_id'] ?? '' ) );
    $ad_group_external_id = trim( (string) ( $ad_group_data['external_ad_group_id'] ?? '' ) );

    if ( $customer_id === '' || $ad_group_external_id === '' )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Brak wymaganych danych Google Ads.' ] );
      exit;
    }

    $api = new \services\GoogleAdsApi();
    if ( !$api -> is_configured() )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Google Ads API nie jest skonfigurowane.' ] );
      exit;
    }

    $api_result = $api -> add_keyword_to_ad_group( $customer_id, $ad_group_external_id, $keyword_text, $match_type );

    if ( !( $api_result['success'] ?? false ) )
    {
      $last_error = \services\GoogleAdsApi::get_setting( 'google_ads_last_error' );
      echo json_encode( self::with_optional_debug( [
        'success' => false,
        'message' => 'Nie udalo sie dodac frazy w Google Ads.',
        'error' => $last_error
      ], [
        'customer_id' => $customer_id,
        'ad_group_external_id' => $ad_group_external_id,
        'keyword_text' => $keyword_text,
        'match_type' => $match_type,
        'api_result' => $api_result
      ] ) );
      exit;
    }

    \factory\Campaigns::insert_campaign_keyword( $campaign_id, $ad_group_id, $keyword_text, $match_type );

    $match_labels = [ 'PHRASE' => 'do wyrazenia', 'EXACT' => 'scisle', 'BROAD' => 'przyblizone' ];
    $match_label = $match_labels[$match_type] ?? $match_type;

    echo json_encode( self::with_optional_debug( [
      'success' => true,
      'message' => ( $api_result['duplicate'] ?? false )
        ? 'Fraza juz istnieje w Google Ads (dopasowanie ' . $match_label . ').'
        : 'Fraza zostala dodana (dopasowanie ' . $match_label . ').',
      'duplicate' => (bool) ( $api_result['duplicate'] ?? false )
    ], [
      'customer_id' => $customer_id,
      'ad_group_external_id' => $ad_group_external_id,
      'keyword_text' => $keyword_text,
      'match_type' => $match_type,
      'api_result' => $api_result
    ] ) );
    exit;
  }

  static public function delete_keyword()
  {
    $keyword_id = (int) \S::get( 'keyword_id' );

    if ( $keyword_id <= 0 )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Nie podano frazy do usuniecia.' ] );
      exit;
    }

    $context = \factory\Campaigns::get_keyword_context( $keyword_id );
    if ( !$context )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Nie znaleziono danych frazy.' ] );
      exit;
    }

    $customer_id = trim( (string) ( $context['google_ads_customer_id'] ?? '' ) );
    $ad_group_external_id = trim( (string) ( $context['external_ad_group_id'] ?? '' ) );
    $keyword_text = trim( (string) ( $context['keyword_text'] ?? '' ) );
    $match_type = strtoupper( trim( (string) ( $context['match_type'] ?? 'BROAD' ) ) );

    if ( $customer_id === '' || $ad_group_external_id === '' || $keyword_text === '' )
    {
      echo json_encode( self::with_optional_debug( [
        'success' => false,
        'message' => 'Brak wymaganych danych Google Ads dla tej frazy.',
      ], [ 'context' => $context ] ) );
      exit;
    }

    $api = new \services\GoogleAdsApi();
    if ( !$api -> is_configured() )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Google Ads API nie jest skonfigurowane.' ] );
      exit;
    }

    $api_result = $api -> remove_keyword_from_ad_group( $customer_id, $ad_group_external_id, $keyword_text, $match_type );

    if ( !( $api_result['success'] ?? false ) )
    {
      $last_error = \services\GoogleAdsApi::get_setting( 'google_ads_last_error' );
      echo json_encode( self::with_optional_debug( [
        'success' => false,
        'message' => 'Nie udalo sie usunac frazy w Google Ads.',
        'error' => $last_error
      ], [
        'customer_id' => $customer_id,
        'ad_group_external_id' => $ad_group_external_id,
        'keyword_text' => $keyword_text,
        'match_type' => $match_type,
        'api_result' => $api_result
      ] ) );
      exit;
    }

    \factory\Campaigns::delete_campaign_keyword( $keyword_id );

    $removed = (int) ( $api_result['removed'] ?? 0 );
    $message = $removed > 0
      ? 'Fraza zostala usunieta z Google Ads.'
      : 'Fraza nie byla juz obecna w Google Ads. Usunieto lokalny wpis.';

    echo json_encode( self::with_optional_debug( [
      'success' => true,
      'message' => $message,
      'removed' => $removed
    ], [
      'customer_id' => $customer_id,
      'ad_group_external_id' => $ad_group_external_id,
      'keyword_text' => $keyword_text,
      'match_type' => $match_type,
      'api_result' => $api_result
    ] ) );
    exit;
  }

  /**
   * Wstrzymuje lub wznawia fraze kluczowa (ENABLED <-> PAUSED).
   */
  static public function toggle_keyword_status()
  {
    $keyword_id = (int) \S::get( 'keyword_id' );

    if ( $keyword_id <= 0 )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Nie podano frazy.' ] );
      exit;
    }

    $context = \factory\Campaigns::get_keyword_context( $keyword_id );
    if ( !$context )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Nie znaleziono danych frazy.' ] );
      exit;
    }

    $customer_id = trim( (string) ( $context['google_ads_customer_id'] ?? '' ) );
    $ad_group_external_id = trim( (string) ( $context['external_ad_group_id'] ?? '' ) );
    $keyword_text = trim( (string) ( $context['keyword_text'] ?? '' ) );
    $match_type = strtoupper( trim( (string) ( $context['match_type'] ?? 'BROAD' ) ) );
    $current_status = strtoupper( trim( (string) ( $context['status'] ?? 'ENABLED' ) ) );

    if ( $customer_id === '' || $ad_group_external_id === '' || $keyword_text === '' )
    {
      echo json_encode( self::with_optional_debug( [
        'success' => false,
        'message' => 'Brak wymaganych danych Google Ads dla tej frazy.',
      ], [ 'context' => $context ] ) );
      exit;
    }

    $new_status = ( $current_status === 'PAUSED' ) ? 'ENABLED' : 'PAUSED';

    $api = new \services\GoogleAdsApi();
    if ( !$api -> is_configured() )
    {
      echo json_encode( [ 'success' => false, 'message' => 'Google Ads API nie jest skonfigurowane.' ] );
      exit;
    }

    $api_result = $api -> update_keyword_status( $customer_id, $ad_group_external_id, $keyword_text, $match_type, $new_status );

    if ( !( $api_result['success'] ?? false ) )
    {
      $last_error = \services\GoogleAdsApi::get_setting( 'google_ads_last_error' );
      echo json_encode( self::with_optional_debug( [
        'success' => false,
        'message' => 'Nie udalo sie zmienic statusu frazy w Google Ads.',
        'error' => $last_error
      ], [
        'customer_id' => $customer_id,
        'ad_group_external_id' => $ad_group_external_id,
        'keyword_text' => $keyword_text,
        'match_type' => $match_type,
        'new_status' => $new_status,
        'api_result' => $api_result
      ] ) );
      exit;
    }

    \factory\Campaigns::update_campaign_keyword_status( $keyword_id, $new_status );

    $status_label = ( $new_status === 'PAUSED' ) ? 'wstrzymana' : 'wznowiona';

    echo json_encode( self::with_optional_debug( [
      'success' => true,
      'message' => 'Fraza zostala ' . $status_label . '.',
      'new_status' => $new_status
    ], [
      'customer_id' => $customer_id,
      'ad_group_external_id' => $ad_group_external_id,
      'keyword_text' => $keyword_text,
      'match_type' => $match_type,
      'api_result' => $api_result
    ] ) );
    exit;
  }
}
