<?php

namespace Win7\Ads\Repository;

use Win7\Ads\Entity\CustomerFilter;
use Win7\Ads\Report\QueryBuilder;
use Win7\Ads\Report\ReportRequest;
use Win7\Ads\Utils\ResourceNames;

/**
 * Repositório responsável por consultar e executar modificações em campanhas.
 *
 * @author Thiago Daher
 */
class CampaignRepository extends AdsRepository
{

    /**
     * Retorna um compilado geral das métricas do cliente.
     *
     * @param \Win7\Ads\Entity\CustomerFilter $filter
     * @return array
     */
    public function getCustomerMetrics(CustomerFilter $filter): array
    {
        $request = new ReportRequest();
        $request->setCustomerId($filter->getCustomerId());

        $qb = $request->getQueryBuilder();
        $this->addDefaultCampaignData($qb, $filter);
        $qb->andWhere('metrics.impressions > 0');

        return $this->getReportResult($request);
    }

    /**
     * Adiciona os dados que são comumente consultados em várias queries.
     *
     * @param \Win7\Ads\Report\QueryBuilder $qb
     * @param \Win7\Ads\Entity\CustomerFilter $filter
     * @return void
     */
    private function addDefaultCampaignData(QueryBuilder $qb, CustomerFilter $filter)
    {
        $qb->andSelect('metrics.absolute_top_impression_percentage');
        $qb->andSelect('metrics.top_impression_percentage');
        $qb->andSelect('metrics.average_cpc');
        $qb->andSelect('metrics.cost_micros');
        $qb->andSelect('metrics.ctr');
        $qb->andSelect('metrics.conversions_from_interactions_rate');
        $qb->andSelect('metrics.conversions');
        $qb->andSelect('metrics.cost_per_conversion');
        $qb->andSelect('metrics.clicks');
        $qb->andSelect('metrics.impressions');
        $qb->andSelect('metrics.invalid_clicks');
        $qb->from('campaign');
        $this->applyCampaignFilter($qb, $filter);
        $this->applyDateFilter($qb, $filter);
    }

    /**
     * Retorna um compilado geral das métricas do cliente de forma semanal.
     *
     * @param \Win7\Ads\Entity\CustomerFilter $filter
     * @return array
     */
    public function getWeeklyCustomerMetrics(CustomerFilter $filter): array
    {
        $request = new ReportRequest();
        $request->setCustomerId($filter->getCustomerId());

        $qb = $request->getQueryBuilder();
        $this->addDefaultCampaignData($qb, $filter);
        $qb->andSelect('segments.week');

        return $this->getReportResult($request);
    }

    /**
     * Retorna um compilado geral das métricas do cliente de forma diária.
     *
     * @param \Win7\Ads\Entity\CustomerFilter $filter
     * @return array
     */
    public function getDailyCustomerMetrics(CustomerFilter $filter): array
    {
        $request = new ReportRequest();
        $request->setCustomerId($filter->getCustomerId());

        $qb = $request->getQueryBuilder();
        $this->addDefaultCampaignData($qb, $filter);
        $qb->andSelect('segments.date');

        return $this->getReportResult($request);
    }

    /**
     * Retorna um compilado geral das métricas do cliente de forma mensal.
     *
     * @param \Win7\Ads\Entity\CustomerFilter $filter
     * @return array
     */
    public function getMonthlyCustomerMetrics(CustomerFilter $filter): array
    {
        $request = new ReportRequest();
        $request->setCustomerId($filter->getCustomerId());

        $qb = $request->getQueryBuilder();
        $this->addDefaultCampaignData($qb, $filter);
        $qb->andSelect('segments.month');

        return $this->getReportResult($request);
    }

    /**
     * Retorna a lista de campanhas, juntamente com as métricas.
     *
     * @param \Win7\Ads\Entity\CustomerFilter $filter
     * @return array
     */
    public function getListWithMetrics(CustomerFilter $filter): array
    {
        $request = new ReportRequest();
        $request->setCustomerId($filter->getCustomerId());

        $qb = $request->getQueryBuilder();
        $qb->andSelect('campaign.name');
        $qb->andSelect('campaign.id');
        $qb->andSelect('campaign.status');
        $this->addDefaultCampaignData($qb, $filter);

        if ($filter->isCampaignPauseAllowed()) {
            $qb->andWhere($qb->expr()->in('campaign.status', ['ENABLED', 'PAUSED']));
        } else {
            $qb->andWhere('metrics.impressions > 0');
        }

        return $this->getReportResult($request);
    }

    /**
     * Retorna a lista de campanhas.
     *
     * @param \Win7\Ads\Entity\CustomerFilter $filter
     * @return array
     */
    public function getList(CustomerFilter $filter): array
    {
        $request = new ReportRequest();
        $request->setCustomerId($filter->getCustomerId());

        $qb = $request->getQueryBuilder();
        $qb->andSelect('campaign.name');
        $qb->andSelect('campaign.id');
        $qb->andSelect('campaign.status');
        $qb->from('campaign');
        $this->applyCampaignFilter($qb, $filter, false);
        $qb->andWhere($qb->expr()->in('campaign.status', ['ENABLED']));

        return $this->getReportResult($request);
    }

    /**
     * Pausa a campanha especificada.
     *
     * @param \Win7\Ads\Entity\CustomerFilter $filter
     * @param string $campaignId
     * @return void
     */
    public function pauseCampaign(CustomerFilter $filter, string $campaignId)
    {
        $operation = [
            'update_mask' => 'status',
            'update'      => [
                'resource_name' => ResourceNames::forCampaign($filter->getCustomerId(), $campaignId),
                'status'        => 'PAUSED',
            ],
        ];

        $this->mutate('campaigns', $filter->getCustomerId(), [$operation]);
    }

    /**
     * Bloqueia o IP na lista de campanhas especificada.
     *
     * @param \Win7\Ads\Entity\CustomerFilter $filter
     * @param array $campaignList lista com ID das campanhas
     * @param string $ip endereço IP
     * @return void
     */
    public function blockIp(CustomerFilter $filter, array $campaignList, string $ip)
    {
        $operations = [];

        foreach ($campaignList as $campaignId) {
            $operations[] = [
                'create' => [
                    'campaign' => ResourceNames::forCampaign($filter->getCustomerId(), $campaignId),
                    'type'     => 'IP_BLOCK',
                    'ip_block' => [
                        'ip_address' => $ip,
                    ],
                    'negative' => true,
                ],
            ];
        }

        $this->mutate('campaignCriteria', $filter->getCustomerId(), $operations);
    }

    /**
     * Despausa a campanha especificada.
     *
     * @param \Win7\Ads\Entity\CustomerFilter $filter
     * @param string $campaignId
     * @return void
     */
    public function resumeCampaign(CustomerFilter $filter, string $campaignId)
    {
        $operation = [
            'update_mask' => 'status',
            'update'      => [
                'resource_name' => ResourceNames::forCampaign($filter->getCustomerId(), $campaignId),
                'status'        => 'ENABLED',
            ],
        ];

        $this->mutate('campaigns', $filter->getCustomerId(), [$operation]);
    }

    /**
     * Retorna a lista de palavras-chave negativas em nível de campanha.
     *
     * @param \Win7\Ads\Entity\CustomerFilter $filter
     * @param string|null $campaignId
     * @return array
     */
    public function getNegativeKeywordList(CustomerFilter $filter, ?string $campaignId): array
    {
        $request = new ReportRequest();
        $request->setCustomerId($filter->getCustomerId());

        $qb = $request->getQueryBuilder();
        $qb->andSelect('campaign.name');
        $qb->andSelect('campaign_criterion.keyword.text');
        $qb->andSelect('campaign_criterion.keyword.match_type');
        $qb->andSelect('campaign_criterion.negative');
        $this->applyCampaignFilter($qb, $filter, false);
        $qb->andWhere($qb->expr()->in('campaign.status', ['ENABLED', 'PAUSED']));
        $qb->andWhere('campaign_criterion.negative = true');
        $qb->andWhere("campaign_criterion.type = 'KEYWORD'");

        if ($campaignId) {
            $qb->andWhere("campaign.id = '$campaignId'");
        }

        $qb->from('campaign_criterion');

        return $this->getReportResult($request);
    }
}