<?php

namespace Tests\Feature;

use App\Models\ApiCall;
use App\Models\GoogleAdsConnection;
use App\Services\GoogleAdsService;
use App\Services\Platforms\Support\CallContext;
use App\Services\Platforms\Support\CallLogger;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\Client\Request;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;

/**
 * A Search campaign is created with settings Search accepts.
 *
 * Publishing campaign 54 on 23 Sep failed the very first mutate with "Request
 * contains an invalid argument. (OPERATION_NOT_PERMITTED_FOR_CONTEXT, UNKNOWN)",
 * naming no field. The request held:
 *
 *   "selectiveOptimization": {"conversionActions": [".../7773535132"]},
 *   "advertisingChannelType": "SEARCH"
 *
 * Selective optimization belongs to App campaigns. It is not how a Search
 * campaign says which conversions it counts, and Google refuses it outright
 * rather than ignoring it, so the campaign, its budget and everything under it
 * never got made.
 *
 * It was sent on Display and Performance Max too. Neither had ever published
 * successfully, so neither had ever proved it was accepted there either.
 */
class GooglePublishSendsWhatSearchAcceptsTest extends TestCase
{
    use RefreshDatabase;

    protected function setUp(): void
    {
        parent::setUp();

        config([
            'services.google_ads.refresh_token' => 'a-refresh-token',
            'services.google_ads.customer_id' => '3460855874',
            'services.google_ads.login_customer_id' => '8039000543',
            'services.google_ads.developer_token' => 'dev-token',
            'platforms.guardrails.allowed_ad_accounts' => ['3460855874'],
        ]);

        Http::fake([
            'oauth2.googleapis.com/*' => Http::response(['access_token' => 'token', 'expires_in' => 3600]),
            'googleads.googleapis.com/*' => Http::response([
                'results' => [['resourceName' => 'customers/3460855874/campaigns/1']],
            ]),
        ]);
    }

    /** @return array<string,mixed> */
    private function createSearchCampaign(): array
    {
        $created = [];

        try {
            app(GoogleAdsService::class)->createPausedCampaign(GoogleAdsConnection::shared(), [
                'campaign_type' => 'search',
                'name' => 'Get Compliance Automated',
                'ad_group_name' => 'Get Compliance Automated - ad group',
                'daily_budget' => 10.0,
                'bidding_strategy' => 'target_cpa',
                'bid_target' => 1.0,
                'final_url' => 'https://iblockads.org/',
                'locations' => ['US'],
                'networks' => ['search'],
                'conversion_action' => 'customers/3460855874/conversionActions/7773535132',
                'keywords' => 'adblocker, block ads',
                'headlines' => ['Block Annoying Ads', 'Faster Browsing', 'AdBlocker Extension'],
                'descriptions' => ['Block pop-ups instantly.', 'Browse faster and safer.'],
            ], $created);
        } catch (\Throwable) {
            // The fake does not model every response; the payload is the point.
        }

        foreach (Http::recorded() as [$request]) {
            /** @var Request $request */
            if (str_contains($request->url(), 'campaigns:mutate')) {
                return $request->data()['operations'][0]['create'] ?? [];
            }
        }

        $this->fail('No campaign was created.');
    }

    public function test_a_search_campaign_does_not_carry_selective_optimization(): void
    {
        $sent = $this->createSearchCampaign();

        $this->assertArrayNotHasKey(
            'selectiveOptimization',
            $sent,
            'Google refuses this on Search and makes nothing at all.',
        );
    }

    /**
     * Everything else the campaign needs is still sent.
     *
     * The fix is a removal, and a removal is the easy thing to overshoot.
     */
    public function test_the_rest_of_the_campaign_is_unchanged(): void
    {
        $sent = $this->createSearchCampaign();

        $this->assertSame('SEARCH', $sent['advertisingChannelType'] ?? null);
        $this->assertSame('PAUSED', $sent['status'] ?? null);
        $this->assertSame('1000000', $sent['targetCpa']['targetCpaMicros'] ?? null);
        $this->assertTrue($sent['networkSettings']['targetGoogleSearch'] ?? false);
        $this->assertNotEmpty($sent['campaignBudget'] ?? null);
    }

    /**
     * The audit row says which platform it was.
     *
     * Every one of the 390 rows on dev said meta, Google's included, because
     * the column was never written and took its default. The guardrails were
     * unaffected, being handed the platform directly; it was only the record of
     * what happened that was wrong, which is the one thing it cannot be.
     */
    public function test_a_google_call_is_recorded_as_google(): void
    {
        app(CallLogger::class)->record(
            new CallContext(
                transport: 'google_ads',
                method: 'POST',
                endpoint: 'customers/3460855874/campaigns:mutate',
                adAccountId: '3460855874',
                mutating: true,
                platform: 'google',
            ),
            fn (): array => ['results' => []],
        );

        $this->assertSame('google', ApiCall::query()->latest('id')->first()?->platform);
    }
}
