<?php
namespace factory;
class Campaigns
{
  static public function get_clients()
  {
    global $mdb;
    return $mdb -> select( 'clients', '*', [ 'ORDER' => [ 'name' => 'ASC' ] ] );
  }

  static public function get_campaigns_list( $client_id, $only_active = false )
  {
    global $mdb;

    $client_id = (int) $client_id;

    if ( !$only_active )
    {
      return $mdb -> select( 'campaigns', '*', [ 'client_id' => $client_id, 'ORDER' => [ 'campaign_name' => 'ASC' ] ] );
    }

    $latest_date = $mdb -> query(
      'SELECT MAX( ch.date_add )
       FROM campaigns_history AS ch
       INNER JOIN campaigns AS c ON c.id = ch.campaign_id
       WHERE c.client_id = :client_id
         AND c.campaign_id <> 0',
      [ ':client_id' => $client_id ]
    ) -> fetchColumn();

    if ( !$latest_date )
    {
      return $mdb -> select( 'campaigns', '*', [ 'client_id' => $client_id, 'ORDER' => [ 'campaign_name' => 'ASC' ] ] );
    }

    return $mdb -> query(
      'SELECT c.*
       FROM campaigns AS c
       LEFT JOIN campaigns_history AS ch
         ON ch.campaign_id = c.id
        AND ch.date_add = :latest_date
       WHERE c.client_id = :client_id
         AND ( c.campaign_id = 0 OR ch.id IS NOT NULL )
       ORDER BY c.campaign_name ASC',
      [
        ':client_id' => $client_id,
        ':latest_date' => $latest_date
      ]
    ) -> fetchAll( \PDO::FETCH_ASSOC );
  }

  static public function get_campaign_history_data( $campaign_id, $start, $length, $revert = false )
  {
    global $mdb;
    if ( $revert )
      return $mdb -> select( 'campaigns_history', '*', [ 'campaign_id' => $campaign_id, 'LIMIT' => [ $start, $length ], 'ORDER' => [ 'date_add' => 'ASC' ] ] );
    else
      return $mdb -> select( 'campaigns_history', '*', [ 'campaign_id' => $campaign_id, 'LIMIT' => [ $start, $length ], 'ORDER' => [ 'date_add' => 'DESC' ] ] );
  }

  static public function get_records_total_campaign_history_data( $campaign_id )
  {
    global $mdb;
    return $mdb -> count( 'campaigns_history', [ 'campaign_id' => $campaign_id ] );
  }

  static public function get_client_name( $client_id )
  {
    global $mdb;
    return $mdb -> get( 'clients', 'name', [ 'id' => $client_id ] );
  }

  static public function get_campaign_ad_groups( $campaign_id )
  {
    global $mdb;

    return $mdb -> query(
      'SELECT
         id,
         campaign_id,
         ad_group_id,
         ad_group_name,
         status,
         impressions_30,
         clicks_30,
         cost_30,
         conversions_30,
         conversion_value_30,
         roas_30,
         impressions_all_time,
         clicks_all_time,
         cost_all_time,
         conversions_all_time,
         conversion_value_all_time,
         roas_all_time
       FROM campaign_ad_groups
       WHERE campaign_id = :campaign_id
         AND status = \'active\'
       ORDER BY clicks_30 DESC, clicks_all_time DESC, ad_group_name ASC',
      [ ':campaign_id' => (int) $campaign_id ]
    ) -> fetchAll( \PDO::FETCH_ASSOC );
  }

  static public function get_campaign_search_terms( $campaign_id, $ad_group_id = 0 )
  {
    global $mdb;

    $sql = 'SELECT
              st.id,
              st.campaign_id,
              st.ad_group_id,
              c.campaign_name,
              c.advertising_channel_type,
              ag.ad_group_name,
              st.search_term,
              st.impressions_30,
              st.clicks_30,
              st.cost_30,
              st.conversions_30,
              st.conversion_value_30,
              st.roas_30,
              st.impressions_all_time,
              st.clicks_all_time,
              st.cost_all_time,
              st.conversions_all_time,
              st.conversion_value_all_time,
              st.roas_all_time
            FROM campaign_search_terms AS st
            LEFT JOIN campaigns AS c ON c.id = st.campaign_id
            LEFT JOIN campaign_ad_groups AS ag ON ag.id = st.ad_group_id
            WHERE st.campaign_id = :campaign_id';

    $params = [ ':campaign_id' => (int) $campaign_id ];

    if ( (int) $ad_group_id > 0 )
    {
      $sql .= ' AND st.ad_group_id = :ad_group_id';
      $params[':ad_group_id'] = (int) $ad_group_id;
    }

    $sql .= ' ORDER BY st.clicks_30 DESC, st.clicks_all_time DESC, st.search_term ASC';

    return $mdb -> query( $sql, $params ) -> fetchAll( \PDO::FETCH_ASSOC );
  }

  static public function get_campaign_negative_keywords( $campaign_id, $ad_group_id = 0 )
  {
    global $mdb;

    $sql = 'SELECT
              nk.id,
              nk.campaign_id,
              nk.ad_group_id,
              ag.ad_group_name,
              nk.scope,
              nk.keyword_text,
              nk.match_type
            FROM campaign_negative_keywords AS nk
            LEFT JOIN campaign_ad_groups AS ag ON ag.id = nk.ad_group_id
            WHERE nk.campaign_id = :campaign_id';

    $params = [ ':campaign_id' => (int) $campaign_id ];

    if ( (int) $ad_group_id > 0 )
    {
      $sql .= ' AND ( nk.scope = \'campaign\' OR nk.ad_group_id = :ad_group_id )';
      $params[':ad_group_id'] = (int) $ad_group_id;
    }

    $sql .= ' ORDER BY nk.scope ASC, nk.keyword_text ASC';

    return $mdb -> query( $sql, $params ) -> fetchAll( \PDO::FETCH_ASSOC );
  }

  static public function get_campaign_keywords( $campaign_id, $ad_group_id = 0 )
  {
    global $mdb;

    $sql = 'SELECT
              kw.id,
              kw.campaign_id,
              kw.ad_group_id,
              ag.ad_group_name,
              kw.keyword_text,
              kw.match_type,
              kw.status,
              kw.impressions_30,
              kw.clicks_30,
              kw.cost_30,
              kw.conversions_30,
              kw.conversion_value_30,
              kw.roas_30,
              kw.impressions_all_time,
              kw.clicks_all_time,
              kw.cost_all_time,
              kw.conversions_all_time,
              kw.conversion_value_all_time,
              kw.roas_all_time
            FROM campaign_keywords AS kw
            LEFT JOIN campaign_ad_groups AS ag ON ag.id = kw.ad_group_id
            WHERE kw.campaign_id = :campaign_id';

    $params = [ ':campaign_id' => (int) $campaign_id ];

    if ( (int) $ad_group_id > 0 )
    {
      $sql .= ' AND kw.ad_group_id = :ad_group_id';
      $params[':ad_group_id'] = (int) $ad_group_id;
    }

    $sql .= ' ORDER BY kw.clicks_30 DESC, kw.clicks_all_time DESC, kw.keyword_text ASC';

    return $mdb -> query( $sql, $params ) -> fetchAll( \PDO::FETCH_ASSOC );
  }

  static public function get_search_term_context( $search_term_row_id )
  {
    global $mdb;

    return $mdb -> query(
      'SELECT
         st.id AS search_term_row_id,
         st.search_term,
         st.campaign_id AS db_campaign_id,
         st.ad_group_id AS db_ad_group_id,
         c.client_id,
         c.campaign_id AS external_campaign_id,
         c.advertising_channel_type,
         ag.ad_group_id AS external_ad_group_id,
         cl.google_ads_customer_id
       FROM campaign_search_terms AS st
       INNER JOIN campaigns AS c ON c.id = st.campaign_id
       INNER JOIN clients AS cl ON cl.id = c.client_id
       LEFT JOIN campaign_ad_groups AS ag ON ag.id = st.ad_group_id
       WHERE st.id = :search_term_row_id
       LIMIT 1',
      [ ':search_term_row_id' => (int) $search_term_row_id ]
    ) -> fetch( \PDO::FETCH_ASSOC );
  }

  static public function upsert_campaign_negative_keyword( $campaign_id, $ad_group_id, $scope, $keyword_text, $match_type )
  {
    global $mdb;

    $campaign_id = (int) $campaign_id;
    $ad_group_id = $ad_group_id !== null ? (int) $ad_group_id : null;
    $scope = $scope === 'campaign' ? 'campaign' : 'ad_group';
    $keyword_text = trim( (string) $keyword_text );
    $match_type = strtoupper( trim( (string) $match_type ) );

    if ( $campaign_id <= 0 || $keyword_text === '' )
    {
      return false;
    }

    $existing = $mdb -> query(
      'SELECT id
       FROM campaign_negative_keywords
       WHERE campaign_id = :campaign_id
         AND ( ( :ad_group_id IS NULL AND ad_group_id IS NULL ) OR ad_group_id = :ad_group_id )
         AND scope = :scope
         AND LOWER(keyword_text) = LOWER(:keyword_text)
         AND UPPER(COALESCE(match_type, \'\')) = :match_type
       LIMIT 1',
      [
        ':campaign_id' => $campaign_id,
        ':ad_group_id' => $ad_group_id,
        ':scope' => $scope,
        ':keyword_text' => $keyword_text,
        ':match_type' => $match_type
      ]
    ) -> fetchColumn();

    if ( $existing )
    {
      return (int) $existing;
    }

    $mdb -> insert( 'campaign_negative_keywords', [
      'campaign_id' => $campaign_id,
      'ad_group_id' => $ad_group_id,
      'scope' => $scope,
      'keyword_text' => $keyword_text,
      'match_type' => $match_type,
      'date_sync' => date( 'Y-m-d' )
    ] );

    return (int) $mdb -> id();
  }

  static public function get_negative_keyword_context( $negative_keyword_row_id )
  {
    global $mdb;

    return $mdb -> query(
      'SELECT
         nk.id AS negative_keyword_row_id,
         nk.scope,
         nk.keyword_text,
         nk.match_type,
         nk.campaign_id AS db_campaign_id,
         nk.ad_group_id AS db_ad_group_id,
         c.client_id,
         c.campaign_id AS external_campaign_id,
         ag.ad_group_id AS external_ad_group_id,
         cl.google_ads_customer_id
       FROM campaign_negative_keywords AS nk
       INNER JOIN campaigns AS c ON c.id = nk.campaign_id
       INNER JOIN clients AS cl ON cl.id = c.client_id
       LEFT JOIN campaign_ad_groups AS ag ON ag.id = nk.ad_group_id
       WHERE nk.id = :negative_keyword_row_id
       LIMIT 1',
      [ ':negative_keyword_row_id' => (int) $negative_keyword_row_id ]
    ) -> fetch( \PDO::FETCH_ASSOC );
  }

  static public function delete_campaign_negative_keyword( $negative_keyword_row_id )
  {
    global $mdb;
    return $mdb -> delete( 'campaign_negative_keywords', [ 'id' => (int) $negative_keyword_row_id ] );
  }

  static public function get_keyword_context( $keyword_id )
  {
    global $mdb;

    return $mdb -> query(
      'SELECT
         kw.id AS keyword_row_id,
         kw.keyword_text,
         kw.match_type,
         kw.status,
         kw.campaign_id AS db_campaign_id,
         kw.ad_group_id AS db_ad_group_id,
         c.client_id,
         c.campaign_id AS external_campaign_id,
         ag.ad_group_id AS external_ad_group_id,
         cl.google_ads_customer_id
       FROM campaign_keywords AS kw
       INNER JOIN campaigns AS c ON c.id = kw.campaign_id
       INNER JOIN clients AS cl ON cl.id = c.client_id
       LEFT JOIN campaign_ad_groups AS ag ON ag.id = kw.ad_group_id
       WHERE kw.id = :keyword_id
       LIMIT 1',
      [ ':keyword_id' => (int) $keyword_id ]
    ) -> fetch( \PDO::FETCH_ASSOC );
  }

  static public function update_keyword_match_type( $keyword_id, $new_match_type )
  {
    global $mdb;
    return $mdb -> update( 'campaign_keywords', [
      'match_type' => strtoupper( trim( (string) $new_match_type ) )
    ], [ 'id' => (int) $keyword_id ] );
  }

  static public function insert_campaign_keyword( $campaign_id, $ad_group_id, $keyword_text, $match_type )
  {
    global $mdb;

    $mdb -> insert( 'campaign_keywords', [
      'campaign_id' => (int) $campaign_id,
      'ad_group_id' => (int) $ad_group_id,
      'keyword_text' => trim( (string) $keyword_text ),
      'match_type' => strtoupper( trim( (string) $match_type ) ),
      'date_sync' => date( 'Y-m-d' )
    ] );

    return (int) $mdb -> id();
  }

  static public function delete_campaign_keyword( $keyword_id )
  {
    global $mdb;
    return $mdb -> delete( 'campaign_keywords', [ 'id' => (int) $keyword_id ] );
  }

  static public function update_campaign_keyword_status( $keyword_id, $status )
  {
    global $mdb;
    return $mdb -> update( 'campaign_keywords', [ 'status' => $status ], [ 'id' => (int) $keyword_id ] );
  }

  static public function delete_campaign( $campaign_id )
  {
    global $mdb;
    $mdb -> delete( 'campaigns_history', [ 'campaign_id' => $campaign_id ] );
    return $mdb -> delete( 'campaigns', [ 'id' => $campaign_id ] );
  }

  static public function delete_campaigns( array $ids )
  {
    global $mdb;
    $deleted = 0;

    foreach ( $ids as $id )
    {
      $mdb -> delete( 'campaigns_history', [ 'campaign_id' => $id ] );
      $result = $mdb -> delete( 'campaigns', [ 'id' => $id ] );
      if ( $result ) $deleted++;
    }

    return $deleted;
  }

  static public function delete_history_entry( $history_id )
  {
    global $mdb;
    return $mdb -> delete( 'campaigns_history', [ 'id' => $history_id ] );
  }

  static public function delete_history_entries( $ids )
  {
    global $mdb;
    $mdb -> delete( 'campaigns_history', [ 'id' => $ids ] );
    return count( $ids );
  }

  static public function get_campaign_comments( $campaign_id )
  {
    global $mdb;
    return $mdb -> query( 'SELECT id, comment, date_add FROM campaigns_comments WHERE campaign_id = \'' . (int) $campaign_id . '\' ORDER BY date_add DESC' ) -> fetchAll( \PDO::FETCH_ASSOC );
  }

  static public function add_campaign_comment( $campaign_id, $comment, $date = null )
  {
    global $mdb;

    if ( !$date )
      $date = date( 'Y-m-d' );
    else
      $date = date( 'Y-m-d', strtotime( $date ) );

    if ( $mdb -> count( 'campaigns_comments', [ 'AND' => [ 'campaign_id' => $campaign_id, 'date_add' => $date ] ] ) )
      return $mdb -> update( 'campaigns_comments', [ 'comment' => $comment ], [ 'AND' => [ 'campaign_id' => $campaign_id, 'date_add' => $date ] ] );
    else
      return $mdb -> insert( 'campaigns_comments', [ 'campaign_id' => $campaign_id, 'comment' => $comment, 'date_add' => $date ] );
  }

  static public function delete_campaign_comment( $comment_id )
  {
    global $mdb;
    return $mdb -> delete( 'campaigns_comments', [ 'id' => $comment_id ] );
  }
}
