<?php

namespace app\service;


use app\util\Helper;
use app\util\ArgumentNames;
use app\util\ArgumentParser;
use Google\Ads\GoogleAds\Lib\V18\GoogleAdsClient;
use Google\Ads\GoogleAds\Lib\V18\GoogleAdsClientBuilder;
use Google\Ads\GoogleAds\Lib\V18\GoogleAdsException;
use Google\Ads\GoogleAds\Lib\OAuth2TokenBuilder;
use Google\Ads\GoogleAds\Lib\V18\GoogleAdsServerStreamDecorator;
use Google\Ads\GoogleAds\Util\FieldMasks;
use Google\Ads\GoogleAds\Util\V18\ResourceNames;
use Google\Ads\GoogleAds\V18\Common\ManualCpc;
use Google\Ads\GoogleAds\V18\Enums\AdvertisingChannelTypeEnum\AdvertisingChannelType;
use Google\Ads\GoogleAds\V18\Enums\BudgetDeliveryMethodEnum\BudgetDeliveryMethod;
use Google\Ads\GoogleAds\V18\Enums\CampaignStatusEnum\CampaignStatus;
use Google\Ads\GoogleAds\V18\Errors\GoogleAdsError;
use Google\Ads\GoogleAds\V18\Resources\Campaign;
use Google\Ads\GoogleAds\V18\Resources\Campaign\NetworkSettings;
use Google\Ads\GoogleAds\V18\Resources\CampaignBudget;
use Google\Ads\GoogleAds\V18\Services\CampaignBudgetOperation;
use Google\Ads\GoogleAds\V18\Services\CampaignOperation;
use Google\Ads\GoogleAds\V18\Services\GoogleAdsRow;
use Google\Ads\GoogleAds\V18\Services\MutateCampaignsRequest;
use Google\Ads\GoogleAds\V18\Services\MutateCampaignBudgetsRequest;
use Google\Ads\GoogleAds\V18\Services\SearchGoogleAdsStreamRequest;
use Google\ApiCore\ApiException;

class GoogleAdsCampaignService
{
    private $googleAdsClient;
    private $customerId;
    private const NUMBER_OF_CAMPAIGNS_TO_ADD = 1;

    public function __construct()
    {
//        $this->customerId = getenv('GOOGLE_ADS_CUSTOMER_ID');

        // OAuth2 Token Authentication
        $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build();

        // Google Ads Client initialization
        $this->googleAdsClient = (new GoogleAdsClientBuilder())
            ->fromFile()
            ->withOAuth2Credential($oAuth2Credential)
            ->build();
    }


    /**
     * Runs the example.
     *
     * @param int $customerId the customer ID
     * @param $options
     * @return mixed
     * @throws ApiException
     */
    public function runAddCampaign(int $customerId, $options): mixed
    {
        $googleAdsClient = $this->googleAdsClient;
        // Creates a single shared budget to be used by the campaigns added below.
        $budgetResourceName = self::addCampaignBudget($googleAdsClient, $customerId, $options);

        // Configures the campaign network options.
        $networkSettings = new NetworkSettings([
            'target_google_search' => true,
            'target_search_network' => true,
            // Enables Display Expansion on Search campaigns. See
            // https://support.google.com/google-ads/answer/7193800 to learn more.
            'target_content_network' => true,
            'target_partner_search_network' => false
        ]);

        $campaignOperations = [];
        for ($i = 0; $i < self::NUMBER_OF_CAMPAIGNS_TO_ADD; $i++) {
            // Creates a campaign.
            // [START add_campaigns_1]
            $campaign = new Campaign([
//                'name' => 'Interplanetary Cruise #' . Helper::getPrintableDatetime(),
                'name' => $options['campaign_name'] . rand(10, 99) . ' #' . Helper::getPrintableDatetime(),
                'advertising_channel_type' => AdvertisingChannelType::SEARCH,
                // Recommendation: Set the campaign to PAUSED when creating it to prevent
                // the ads from immediately serving. Set to ENABLED once you've added
                // targeting and the ads are ready to serve.
                'status' => CampaignStatus::PAUSED,
                // Sets the bidding strategy and budget.
                'manual_cpc' => new ManualCpc(),
                'campaign_budget' => $budgetResourceName,
                // Adds the network settings configured above.
                'network_settings' => $networkSettings,
                // Optional: Sets the start and end dates.
                'start_date' => date('Ymd', strtotime('+1 day')),
                'end_date' => date('Ymd', strtotime('+1 month'))
            ]);
            // [END add_campaigns_1]

            // Creates a campaign operation.
            $campaignOperation = new CampaignOperation();
            $campaignOperation->setCreate($campaign);
            $campaignOperations[] = $campaignOperation;
        }

        // Issues a mutate request to add campaigns.
        $campaignServiceClient = $googleAdsClient->getCampaignServiceClient();
        $response              = $campaignServiceClient->mutateCampaigns(
            MutateCampaignsRequest::build($customerId, $campaignOperations)
        );
//        printf("Added %d campaigns:%s", $response->getResults()->count(), PHP_EOL);
//
        foreach ($response->getResults() as $addedCampaign) {
            /** @var Campaign $addedCampaign */
//            print "{$addedCampaign->getResourceName()}" . PHP_EOL;
            $resourceNames[] = $addedCampaign->getResourceName();
        }
        return $resourceNames;
    }

    /**
     * Runs the example.
     *
     * @param int $customerId the customer ID
     * @param $options
     * @return mixed
     * @throws ApiException
     */
    public function runAddCampaignBudget(int $customerId, $options): mixed
    {
//        if(!$customerId){
//            $customerId = $this->customerId;
//        }
        $googleAdsClient = $this->googleAdsClient;
        // Creates a single shared budget to be used by the campaigns added below.
        $budgetResourceName = self::addCampaignBudget($googleAdsClient, $customerId, $options);

        return $budgetResourceName;
    }

    /**
     * Creates a new campaign budget in the specified client account.
     *
     * @param GoogleAdsClient $googleAdsClient the Google Ads API client
     * @param int $customerId the customer ID
     * @return string the resource name of the newly created budget
     * @throws ApiException
     */
    // [START add_campaigns]
    private static function addCampaignBudget(GoogleAdsClient $googleAdsClient, int $customerId, $options)
    {

        // Creates a campaign budget.
        $budget = new CampaignBudget([
//            'name' => 'Interplanetary Cruise Budget #' . Helper::getPrintableDatetime(),
            'name' => $options['name'] . rand(10, 99) . ' #' . Helper::getPrintableDatetime(),
//            'delivery_method' => BudgetDeliveryMethod::STANDARD,
            'amount_micros' => $options['amount'] * 1000000
        ]);

        // Creates a campaign budget operation.
        $campaignBudgetOperation = new CampaignBudgetOperation();
        $campaignBudgetOperation->setCreate($budget);

        // Issues a mutate request.
        $campaignBudgetServiceClient = $googleAdsClient->getCampaignBudgetServiceClient();
        $response                    = $campaignBudgetServiceClient->mutateCampaignBudgets(
            MutateCampaignBudgetsRequest::build($customerId, [$campaignBudgetOperation])
        );

        /** @var CampaignBudget $addedBudget */
        $addedBudget = $response->getResults()[0];
//        printf("Added budget named '%s'%s", $addedBudget->getResourceName(), PHP_EOL);

        return $addedBudget->getResourceName();
    }

    // [END add_campaigns]

    /* @param int $customerId the customer ID
     * @param $options
     * @return mixed
     * @throws ApiException
     */
    public function runListCampaigns(int $customerId): mixed
    {
        $googleAdsClient = $this->googleAdsClient;
        // Creates a single shared budget to be used by the campaigns added below.
        $campaignsResourceName = self::getCampaigns($googleAdsClient, $customerId);

        return $campaignsResourceName;
    }


    /**
     * Runs the example.
     *
     * @param GoogleAdsClient $googleAdsClient the Google Ads API client
     * @param int $customerId the customer ID
     */
    // [START get_campaigns]

    public static function getCampaigns(GoogleAdsClient $googleAdsClient, int $customerId)
    {
        $googleAdsServiceClient = $googleAdsClient->getGoogleAdsServiceClient();
        // Creates a query that retrieves all campaigns.
        $query = 'SELECT campaign.id, campaign.name FROM campaign ORDER BY campaign.id';
        // Issues a search stream request.
        /** @var GoogleAdsServerStreamDecorator $stream */
        $stream        = $googleAdsServiceClient->searchStream(
            SearchGoogleAdsStreamRequest::build($customerId, $query)
        );
        $resourceNames = [];
        // Iterates over all rows in all messages and prints the requested field values for
        // the campaign in each row.
        foreach ($stream->iterateAllElements() as $googleAdsRow) {
            /** @var GoogleAdsRow $googleAdsRow */
            printf(
                "Campaign with ID %d and name '%s' was found.%s",
                $googleAdsRow->getCampaign()->getId(),
                $googleAdsRow->getCampaign()->getName(),
                PHP_EOL
            );
            $resourceNames[$googleAdsRow->getCampaign()->getId()] = $googleAdsRow->getCampaign()->getName();
        }
        return $resourceNames;
    }
    // [END get_campaigns]


    /* @param int $customerId the customer ID
     * @param $options
     * @return mixed
     * @throws ApiException
     */
    public function runRemoveCampaign($options): mixed
    {
        $googleAdsClient = $this->googleAdsClient;
        // Creates a single shared budget to be used by the campaigns added below.
        $resourceName = self::removeCampaign($googleAdsClient, $options['customer_id'], $options['campaign_id']);

        return $resourceName;
    }

    /**
     * Runs the example.
     *
     * @param GoogleAdsClient $googleAdsClient the Google Ads API client
     * @param int $customerId the customer ID
     * @param int $campaignId the ID of the campaign to remove
     */
    public static function removeCampaign(
        GoogleAdsClient $googleAdsClient,
        int             $customerId,
        int             $campaignId
    )
    {
        // Creates the resource name of a campaign to remove.
        $campaignResourceName = ResourceNames::forCampaign($customerId, $campaignId);

        // Creates a campaign operation.
        $campaignOperation = new CampaignOperation();
        $campaignOperation->setRemove($campaignResourceName);

        // Issues a mutate request to remove the campaign.
        $campaignServiceClient = $googleAdsClient->getCampaignServiceClient();
        $response              = $campaignServiceClient->mutateCampaigns(
            MutateCampaignsRequest::build($customerId, [$campaignOperation])
        );

        /** @var Campaign $removedCampaign */
        $removedCampaign = $response->getResults()[0];
        printf(
            "Removed campaign with resource name '%s'%s",
            $removedCampaign->getResourceName(),
            PHP_EOL
        );
        return $removedCampaign->getResourceName();
    }

    /**
     * This example updates a campaign by setting the status to `PAUSED`. To get campaigns, run
     * GetCampaigns.php.
     */
    /* @param int $customerId the customer ID
     * @param $options
     * @return mixed
     * @throws ApiException
     */
    public function runUpdateCampaign($options): mixed
    {
        $googleAdsClient = $this->googleAdsClient;
        // Creates a single shared budget to be used by the campaigns added below.
        $resourceName = self::updateCampaign($googleAdsClient, $options['customer_id'], $options['campaign_id'],$options['status']);

        return $resourceName;
    }

    /**
     * Runs the updateCampaign example.
     *
     * @param GoogleAdsClient $googleAdsClient the Google Ads API client
     * @param int $customerId the customer ID
     * @param int $campaignId the ID of campaign to update
     */
    public static function updateCampaign(
        GoogleAdsClient $googleAdsClient,
        int             $customerId,
        int             $campaignId,
        int             $status
    )
    {

        // Creates a campaign object with the specified resource name and other changes.
        $campaign = new Campaign([
//            'resource_name' => ResourceNames::forCampaign($customerId, $campaignId),
//            'status' => CampaignStatus::PAUSED
            'resource_name' => ResourceNames::forCampaign($customerId, $campaignId),
            'status' =>  $status,
        ]);

        // Constructs an operation that will update the campaign with the specified resource name,
        // using the FieldMasks utility to derive the update mask. This mask tells the Google Ads
        // API which attributes of the campaign you want to change.
        $campaignOperation = new CampaignOperation();
        $campaignOperation->setUpdate($campaign);
        $campaignOperation->setUpdateMask(FieldMasks::allSetFieldsOf($campaign));

        // Issues a mutate request to update the campaign.
        $campaignServiceClient = $googleAdsClient->getCampaignServiceClient();
        $response              = $campaignServiceClient->mutateCampaigns(MutateCampaignsRequest::build(
            $customerId,
            [$campaignOperation]
        ));

        // Prints the resource name of the updated campaign.
        /** @var Campaign $updatedCampaign */
        $updatedCampaign = $response->getResults()[0];
        printf(
            "Updated campaign with resource name: '%s'%s",
            $updatedCampaign->getResourceName(),
            PHP_EOL
        );
        return $updatedCampaign->getResourceName();
    }
}